Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 656cf2d07d | |||
| bfe51f6ad4 | |||
| 2a3107a78d | |||
| 48826e21d6 | |||
| 15c476c12d | |||
| 6c4d7244c3 | |||
| 9fb80102c8 | |||
| 7028258084 | |||
| 5966eb0ffc | |||
| 90c8db0b04 | |||
| 9240bf1243 | |||
| 88f8dcb0e7 | |||
| 9cb3c79dbe | |||
| 75e3b59442 | |||
| 030c2307c2 | |||
| 27d54f15a2 | |||
| 5e18c2b766 | |||
| 1c85341b1f | |||
| ef02839ea0 | |||
| 377927b878 | |||
| a251e56c59 | |||
| 7de018f7eb | |||
| abb25f56d1 | |||
| 7a92231614 | |||
| f2952c905a | |||
| 8abd40f217 | |||
| 34cc87a5f9 | |||
| f986fdcddd | |||
| a212717c37 | |||
| 49cb354133 | |||
| 7298d89c9a | |||
| 3a2ae82593 | |||
| 16717acdd3 | |||
| d96123e661 | |||
| 8a0da95ed2 | |||
| 8363b1b6c8 | |||
| 2874119c6d | |||
| 56fa2fc7f7 | |||
| 5deb9e3255 | |||
| ecd8eacb1f | |||
| 1811c0ec35 | |||
| fae3ff5028 | |||
| 20a24b7935 | |||
| 526022e2bc | |||
| 4a1f4d02f0 | |||
| 4d2a80ddb8 | |||
| 81df32963f | |||
| 2b98ddeadd | |||
| 4c999bee3f | |||
| ba1cca9521 | |||
| b797877558 | |||
| 326644bbac | |||
| 36fdf9af82 | |||
| 579574acb3 | |||
| 63ab74b4fb | |||
| df7e943a74 | |||
| 203356aca8 | |||
| e2bed00b5c | |||
| fdc5849a69 | |||
| 2f8c374569 |
@@ -0,0 +1,150 @@
|
||||
# When a release is published on this repo (or manual dispatch):
|
||||
# 1. Builds the Electron launcher from that tag (npm run pack:win).
|
||||
# 2. Downloads any assets attached to the same release on this repo (patches, Wow exe, …).
|
||||
# 3. Merges them (launcher files win on name collision) and creates/updates the matching
|
||||
# release on Fractured-Distro.
|
||||
#
|
||||
# Setup (GitHub → Settings → Secrets and variables → Actions):
|
||||
# DISTRO_SYNC_TOKEN — PAT with releases write on Fractured-Distro (see repo README).
|
||||
#
|
||||
# Change DISTRO_REPO or the job `if:` if your GitHub slugs differ.
|
||||
|
||||
name: Sync release to Fractured-Distro
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release tag on this repo (must exist; e.g. v1.0.0)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
DISTRO_REPO: Dawnforger/Fractured-Distro
|
||||
|
||||
jobs:
|
||||
meta:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'Dawnforger/Fractured'
|
||||
outputs:
|
||||
tag: ${{ steps.t.outputs.tag }}
|
||||
steps:
|
||||
- name: Resolve tag
|
||||
id: t
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build-electron:
|
||||
needs: meta
|
||||
if: github.repository == 'Dawnforger/Fractured'
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.meta.outputs.tag }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: npm
|
||||
cache-dependency-path: tools/fractured-launcher-electron/package-lock.json
|
||||
|
||||
- name: Install and pack (NSIS + portable)
|
||||
working-directory: tools/fractured-launcher-electron
|
||||
run: |
|
||||
npm ci
|
||||
npm run pack:win
|
||||
|
||||
- name: Stage launcher files for upload
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path launcher-publish | Out-Null
|
||||
Copy-Item tools/fractured-launcher-electron/dist/*.exe launcher-publish/
|
||||
if (Test-Path tools/fractured-launcher-electron/dist/latest.yml) {
|
||||
Copy-Item tools/fractured-launcher-electron/dist/latest.yml launcher-publish/
|
||||
}
|
||||
Get-ChildItem tools/fractured-launcher-electron/dist/*.blockmap -ErrorAction SilentlyContinue |
|
||||
Copy-Item -Destination launcher-publish/
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: electron-dist
|
||||
path: launcher-publish/
|
||||
|
||||
sync-distro:
|
||||
needs: [meta, build-electron]
|
||||
if: github.repository == 'Dawnforger/Fractured'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: electron-dist
|
||||
path: /tmp/electron
|
||||
|
||||
- name: Merge main release assets + Electron build
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="${{ needs.meta.outputs.tag }}"
|
||||
mkdir -p combined
|
||||
mkdir -p /tmp/from-main
|
||||
if gh release download "$TAG" -R "${{ github.repository }}" -D /tmp/from-main 2>/tmp/dl.err; then
|
||||
shopt -s nullglob
|
||||
for f in /tmp/from-main/*; do
|
||||
if [ -f "$f" ]; then
|
||||
cp -f "$f" combined/
|
||||
fi
|
||||
done
|
||||
echo "Merged assets from ${{ github.repository }} release $TAG"
|
||||
else
|
||||
echo "Main release download note (continuing with launcher only):"
|
||||
cat /tmp/dl.err || true
|
||||
fi
|
||||
shopt -s nullglob
|
||||
for f in /tmp/electron/*; do
|
||||
if [ -f "$f" ]; then
|
||||
cp -f "$f" combined/
|
||||
fi
|
||||
done
|
||||
echo "Combined directory:"
|
||||
ls -la combined/
|
||||
|
||||
- name: Upload to Fractured-Distro
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.DISTRO_SYNC_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${GH_TOKEN:-}" ]; then
|
||||
echo "Missing secret DISTRO_SYNC_TOKEN (PAT with access to $DISTRO_REPO)."
|
||||
exit 1
|
||||
fi
|
||||
TAG="${{ needs.meta.outputs.tag }}"
|
||||
shopt -s nullglob
|
||||
files=(combined/*)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "Nothing to upload (Electron pack produced no files?)."
|
||||
exit 1
|
||||
fi
|
||||
SRC_URL="https://github.com/${{ github.repository }}/releases/tag/${TAG}"
|
||||
if gh release view "$TAG" -R "$DISTRO_REPO" &>/dev/null; then
|
||||
gh release upload "$TAG" -R "$DISTRO_REPO" "${files[@]}" --clobber
|
||||
echo "Uploaded (clobber) to $DISTRO_REPO release $TAG"
|
||||
else
|
||||
gh release create "$TAG" -R "$DISTRO_REPO" \
|
||||
--title "Fractured $TAG" \
|
||||
--notes "Synced from [$TAG]($SRC_URL) on ${{ github.repository }}. Includes CI-built Electron launcher + release assets." \
|
||||
"${files[@]}"
|
||||
echo "Created $DISTRO_REPO release $TAG with ${#files[@]} asset(s)."
|
||||
fi
|
||||
@@ -0,0 +1,81 @@
|
||||
# Verifies Electron launcher Windows pack and uploads installers for testing.
|
||||
|
||||
name: Fractured launcher CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [master, main]
|
||||
paths:
|
||||
- 'tools/fractured-launcher-electron/**'
|
||||
- '.github/workflows/fractured-launcher-ci.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'tools/fractured-launcher-electron/**'
|
||||
- '.github/workflows/fractured-launcher-ci.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: fractured-launcher-ci-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
electron-launcher-windows:
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 45
|
||||
defaults:
|
||||
run:
|
||||
working-directory: tools/fractured-launcher-electron
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: npm
|
||||
cache-dependency-path: tools/fractured-launcher-electron/package-lock.json
|
||||
|
||||
- name: Install and pack (NSIS + portable)
|
||||
run: |
|
||||
npm ci
|
||||
npm run pack:win
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fractured-launcher-electron-windows-${{ github.run_id }}
|
||||
if-no-files-found: warn
|
||||
path: |
|
||||
tools/fractured-launcher-electron/dist/*.exe
|
||||
tools/fractured-launcher-electron/dist/latest.yml
|
||||
tools/fractured-launcher-electron/dist/*.blockmap
|
||||
|
||||
electron-launcher-linux:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
defaults:
|
||||
run:
|
||||
working-directory: tools/fractured-launcher-electron
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: npm
|
||||
cache-dependency-path: tools/fractured-launcher-electron/package-lock.json
|
||||
|
||||
- name: Install and pack (AppImage)
|
||||
run: |
|
||||
npm ci
|
||||
npm run pack:linux
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fractured-launcher-electron-linux-${{ github.run_id }}
|
||||
if-no-files-found: warn
|
||||
path: |
|
||||
tools/fractured-launcher-electron/dist/*.AppImage
|
||||
tools/fractured-launcher-electron/dist/*.yml
|
||||
tools/fractured-launcher-electron/dist/*.blockmap
|
||||
@@ -0,0 +1,224 @@
|
||||
# Primary path for player-facing binaries: every *published* GitHub Release on this repo
|
||||
# is mirrored to your self-hosted Gitea (same tag). No public GitHub distro repo.
|
||||
#
|
||||
# Triggers:
|
||||
# - release: published / released → GitHub “Release” (not a raw git tag alone).
|
||||
# - workflow_dispatch → Actions → this workflow → “Run workflow” (enter tag).
|
||||
#
|
||||
# Troubleshooting: “Re-run failed jobs” on an OLD run replays the *original* workflow
|
||||
# YAML (e.g. still runs `npm run pack:win` without --publish never). After changing this
|
||||
# file on default branch, start a *new* run via “Run workflow”, not Re-run on a pre-fix run.
|
||||
#
|
||||
# Important: pushing only a git tag does NOT run this — you must create/publish a
|
||||
# Release on github.com (Releases → Draft/new release → Publish). The workflow
|
||||
# definition must exist on the repo DEFAULT branch (GitHub runs it from there).
|
||||
#
|
||||
# Steps: Windows (NSIS+portable) + Linux (AppImage) in parallel, launcher from DEFAULT BRANCH
|
||||
# overlay on tag checkout → merge with GitHub release assets → upload all to Gitea.
|
||||
#
|
||||
# Secrets: GITEA_BASE_URL, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO
|
||||
# Optional variable: GITEA_TARGET_REF (see tools/fractured-launcher-electron/README.md)
|
||||
#
|
||||
# Job guard: edit `if:` if github.repository is not Dawnforger/Fractured.
|
||||
|
||||
name: Sync release to Gitea
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published, released]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release tag on this GitHub repo (must exist; e.g. v1.0.0)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: gitea-release-sync-${{ github.repository }}-${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.event.release.tag_name }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
meta:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'Dawnforger/Fractured'
|
||||
outputs:
|
||||
tag: ${{ steps.t.outputs.tag }}
|
||||
steps:
|
||||
- name: Resolve tag
|
||||
id: t
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "tag=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build-electron:
|
||||
needs: meta
|
||||
if: github.repository == 'Dawnforger/Fractured'
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.meta.outputs.tag }}
|
||||
|
||||
# Release tags often point at server/game commits that predate launcher lib fixes.
|
||||
# Always pack the launcher from default branch so app.asar includes the full tree.
|
||||
- name: Overlay launcher from default branch
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
DB="${{ github.event.repository.default_branch }}"
|
||||
git fetch --no-tags --depth=1 origin "+refs/heads/${DB}:refs/remotes/origin/${DB}"
|
||||
git checkout "origin/${DB}" -- tools/fractured-launcher-electron
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: npm
|
||||
cache-dependency-path: tools/fractured-launcher-electron/package-lock.json
|
||||
|
||||
- name: Install and pack (NSIS + portable)
|
||||
working-directory: tools/fractured-launcher-electron
|
||||
run: |
|
||||
npm ci
|
||||
npm run pack:win
|
||||
|
||||
- name: Stage launcher files for upload
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path launcher-publish | Out-Null
|
||||
Copy-Item tools/fractured-launcher-electron/dist/*.exe launcher-publish/
|
||||
if (Test-Path tools/fractured-launcher-electron/dist/latest.yml) {
|
||||
Copy-Item tools/fractured-launcher-electron/dist/latest.yml launcher-publish/
|
||||
}
|
||||
Get-ChildItem tools/fractured-launcher-electron/dist/*.blockmap -ErrorAction SilentlyContinue |
|
||||
Copy-Item -Destination launcher-publish/
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: electron-dist-windows
|
||||
path: launcher-publish/
|
||||
|
||||
build-electron-linux:
|
||||
needs: meta
|
||||
if: github.repository == 'Dawnforger/Fractured'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.meta.outputs.tag }}
|
||||
|
||||
- name: Overlay launcher from default branch
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
DB="${{ github.event.repository.default_branch }}"
|
||||
git fetch --no-tags --depth=1 origin "+refs/heads/${DB}:refs/remotes/origin/${DB}"
|
||||
git checkout "origin/${DB}" -- tools/fractured-launcher-electron
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: npm
|
||||
cache-dependency-path: tools/fractured-launcher-electron/package-lock.json
|
||||
|
||||
- name: Install and pack (AppImage)
|
||||
working-directory: tools/fractured-launcher-electron
|
||||
run: |
|
||||
npm ci
|
||||
npm run pack:linux
|
||||
|
||||
- name: Stage Linux launcher for upload
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p launcher-linux-publish
|
||||
shopt -s nullglob
|
||||
cp -f tools/fractured-launcher-electron/dist/*.AppImage launcher-linux-publish/ 2>/dev/null || true
|
||||
cp -f tools/fractured-launcher-electron/dist/*.yml launcher-linux-publish/ 2>/dev/null || true
|
||||
cp -f tools/fractured-launcher-electron/dist/*.blockmap launcher-linux-publish/ 2>/dev/null || true
|
||||
ls -la launcher-linux-publish/
|
||||
if ! compgen -G "launcher-linux-publish/*.AppImage" > /dev/null; then
|
||||
echo "No AppImage under dist/ — electron-builder linux target failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: electron-dist-linux
|
||||
path: launcher-linux-publish/
|
||||
|
||||
sync-gitea:
|
||||
needs: [meta, build-electron, build-electron-linux]
|
||||
if: github.repository == 'Dawnforger/Fractured'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITEA_BASE_URL: ${{ secrets.GITEA_BASE_URL }}
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_OWNER: ${{ secrets.GITEA_OWNER }}
|
||||
GITEA_REPO: ${{ secrets.GITEA_REPO }}
|
||||
GITEA_TARGET_REF: ${{ vars.GITEA_TARGET_REF }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Script may not exist on older release tags; always use default branch.
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
sparse-checkout: |
|
||||
tools/fractured-launcher-electron/scripts
|
||||
sparse-checkout-cone-mode: true
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: electron-dist-windows
|
||||
path: /tmp/electron-win
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: electron-dist-linux
|
||||
path: /tmp/electron-linux
|
||||
|
||||
- name: Merge GitHub release assets + Electron build
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="${{ needs.meta.outputs.tag }}"
|
||||
mkdir -p combined
|
||||
mkdir -p /tmp/from-main
|
||||
if gh release download "$TAG" -R "${{ github.repository }}" -D /tmp/from-main 2>/tmp/dl.err; then
|
||||
shopt -s nullglob
|
||||
for f in /tmp/from-main/*; do
|
||||
if [ -f "$f" ]; then
|
||||
cp -f "$f" combined/
|
||||
fi
|
||||
done
|
||||
echo "Merged assets from ${{ github.repository }} release $TAG"
|
||||
else
|
||||
echo "GitHub release download note (continuing with launcher only):"
|
||||
cat /tmp/dl.err || true
|
||||
fi
|
||||
shopt -s nullglob
|
||||
for f in /tmp/electron-win/* /tmp/electron-linux/*; do
|
||||
if [ -f "$f" ]; then
|
||||
cp -f "$f" combined/
|
||||
fi
|
||||
done
|
||||
ls -la combined/
|
||||
|
||||
- name: Upload to Gitea
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for v in GITEA_BASE_URL GITEA_TOKEN GITEA_OWNER GITEA_REPO; do
|
||||
if [ -z "${!v:-}" ]; then
|
||||
echo "Missing secret $v — add it under repo Settings → Secrets and variables → Actions." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
bash tools/fractured-launcher-electron/scripts/upload-release-to-gitea.sh combined "${{ needs.meta.outputs.tag }}"
|
||||
@@ -23,6 +23,7 @@
|
||||
/data/sql/custom/*
|
||||
/src/server/scripts/Custom/*
|
||||
!/src/server/scripts/Custom/README.md
|
||||
!/src/server/scripts/Custom/custom_script_loader.cpp
|
||||
|
||||
/*.override.yml
|
||||
/*.override.yaml
|
||||
@@ -119,3 +120,9 @@ local.properties
|
||||
!/modules/mod-ale/**
|
||||
# Team Docker: ALE needs Lua at configure time (see mod-ale docs)
|
||||
!docker-compose.override.yml
|
||||
|
||||
# Local build artifacts (do not commit)
|
||||
build-worldserver.log
|
||||
|
||||
# Local logs parked under contrib/fractured-dev-extras (see README there)
|
||||
contrib/fractured-dev-extras/*.log
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ include(ConfigInstall)
|
||||
CU_RUN_HOOK("AFTER_LOAD_CMAKE_MODULES")
|
||||
|
||||
find_package(PCHSupport)
|
||||
find_package(MySQL REQUIRED)
|
||||
find_package(MySQL REQUIRED COMPONENTS lib)
|
||||
|
||||
if(UNIX AND WITH_PERFTOOLS)
|
||||
find_package(Gperftools)
|
||||
|
||||
@@ -15,6 +15,58 @@ prerequisites; everything here is just the deltas you need on top of it.
|
||||
|
||||
---
|
||||
|
||||
## Fractured client + network defaults
|
||||
|
||||
Stock-friendly defaults for fresh local installs. A `git clone` ->
|
||||
`docker compose up` (or native install) lets a single developer log in
|
||||
from the same machine without any post-install config tweaks.
|
||||
|
||||
- **`authserver.conf` -> `RealmServerPort`** = **3724** (stock WoW). A
|
||||
patched `Wow.exe` with `set realmlist 127.0.0.1` (no port) reaches
|
||||
the auth handshake.
|
||||
- **`realmlist` table -> `port`** is the **world** port (default
|
||||
**8085**, matches `WorldServerPort` in `worldserver.conf.dist`).
|
||||
Auth tells the client to handshake to this port for the world hand-off.
|
||||
- **`realmlist` table -> `address`** defaults to **`127.0.0.1`** in the
|
||||
base SQL. The auth server hands this address to clients after login,
|
||||
so 127.0.0.1 means "talk to the world server on the same machine
|
||||
auth is running on" -- correct for solo dev. **Override on production
|
||||
deploys**, see *Production deployment overrides* below.
|
||||
|
||||
### Production deployment overrides
|
||||
|
||||
Production Fractured runs on a remote VPS at `hsrwow.net` with auth
|
||||
bound to a non-stock port (47497 -- 3724 was unavailable on that host).
|
||||
Apply the overrides **once per fresh dbimport** on the production box.
|
||||
|
||||
```sql
|
||||
-- Run against acore_auth on the production database after first dbimport:
|
||||
UPDATE realmlist
|
||||
SET address = 'hsrwow.net',
|
||||
port = 8085 -- world port; leave at 8085 unless changed
|
||||
WHERE id = 1;
|
||||
```
|
||||
|
||||
Edit the production `authserver.conf` (NOT `authserver.conf.dist`)
|
||||
to bind the auth listener to the production port:
|
||||
|
||||
```ini
|
||||
RealmServerPort = 47497
|
||||
```
|
||||
|
||||
Restart the auth server. Production clients connect with:
|
||||
|
||||
```text
|
||||
set realmlist hsrwow.net:47497
|
||||
```
|
||||
|
||||
The Fractured-patched 3.3.5 client supports the `host:port` syntax;
|
||||
stock 3.3.5 clients do not, so any contributor distributing the
|
||||
client bundle for production must include the patched `Wow.exe` from
|
||||
the GitHub release.
|
||||
|
||||
---
|
||||
|
||||
## What you get when you build this fork
|
||||
|
||||
- Worldserver with `CLASS_PARAGON` and Paragon-aware DK rune / sticky
|
||||
@@ -172,22 +224,19 @@ The standard AzerothCore three-database layout (`acore_auth`,
|
||||
|
||||
Easiest path: run the worldserver once with empty databases and let
|
||||
`AC_FORCE_CREATE_DB=1` populate them, **or** use `dbimport` from the
|
||||
install dir. Then layer the Fractured-specific SQL on top:
|
||||
install dir.
|
||||
|
||||
```bash
|
||||
cd <repo>/modules/mod-paragon/sql
|
||||
The Fractured-specific SQL (mod-paragon) lives under
|
||||
`modules/mod-paragon/data/sql/db-{world,characters}/base/` — that's the
|
||||
**AzerothCore-standard module layout**, which means AC's built-in
|
||||
DBUpdater picks it up automatically on every `worldserver` /
|
||||
`dbimport` start. New SQL files are applied; previously-applied ones
|
||||
are skipped via the hash recorded in `acore_world.updates` /
|
||||
`acore_characters.updates`. No manual `mysql < ...` steps required for
|
||||
deploys.
|
||||
|
||||
# world DB
|
||||
mysql -u acore -p acore_world < world/base/paragon_gametables.sql
|
||||
mysql -u acore -p acore_world < world/base/paragon_spell_ae_cost.sql
|
||||
mysql -u acore -p acore_world < world/base/player_class_stats_paragon_basemana.sql
|
||||
|
||||
# characters DB
|
||||
mysql -u acore -p acore_characters < characters/base/character_paragon_currency.sql
|
||||
```
|
||||
|
||||
Re-run any module-specific SQL whenever you pull updates that touch
|
||||
`modules/*/sql/`.
|
||||
Modify-and-redeploy works the same way: change the file, push, pull on
|
||||
the VPS, and the next `dbimport` run sees the new hash and re-applies.
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
# Fractured Client Patches
|
||||
|
||||
Binary client artifacts that pair with this server. They live on the
|
||||
[Releases page](https://github.com/Dawnforger/Fractured/releases),
|
||||
**not** in the tree, so the repo stays lean and binaries can be
|
||||
re-downloaded without bloating `git clone`.
|
||||
|
||||
This file is the table of contents and install guide.
|
||||
|
||||
**Launcher (Windows):** The maintained client launcher lives in
|
||||
[`tools/fractured-launcher-electron/`](../../tools/fractured-launcher-electron/)
|
||||
(see its README for build and config). **Public downloads** for the launcher
|
||||
and mirrored patch assets are pushed to
|
||||
[Fractured-Distro releases](https://github.com/Dawnforger/Fractured-Distro/releases)
|
||||
when a release is published here (workflow **Sync release to Fractured-Distro**).
|
||||
|
||||
---
|
||||
|
||||
## What ships in a release
|
||||
|
||||
| Artifact | Size | Purpose |
|
||||
|---|---|---|
|
||||
| `patch-enUS-4.MPQ` | ~5 MB | DBC + GlueXML bake. Adds `CLASS_PARAGON` (id 12), the character-create slot, glue strings, game-table DBCs, and a patched `Spell.dbc`: **(1)** `RuneCostID` zeroed on every rune-cost spell so non–Death Knight clients still send DK casts (rune costs are shown via `RuneFrame.lua`); **(2)** `Reagent[]` / `ReagentCount[]` zeroed on every spell whose `SpellFamilyName` is non-zero (all class abilities), while profession crafts (`SpellFamilyName == 0`) keep their materials. Both edits mirror server load-time corrections so client preflight and server validation stay aligned. Required for character creation as Paragon to even show up. |
|
||||
| `patch-enUS-5.MPQ` | ~57 KB | FrameXML overrides. Replaces stock `PlayerFrame.lua` / `RuneFrame.lua` / `ComboFrame.lua` / `UnitFrame.lua` / `SpellBookFrame.lua` + `SpellBookFrame.xml` with Paragon-aware versions: rune simulator, combo-point simulator, server-authoritative resource sync over the `PARAA` addon channel, action-button usability + click guards, an expanded spellbook (higher `MAX_SPELLS`, 24 skill-line tabs instead of stock 8) so all-class spells render, Paragon stat tooltips on the character sheet (including filtering duplicate “attack power from strength” lines so the paper doll matches server AP), a tooltip post-processor that appends ", Paragon" to the "Classes:" line on class-restricted gear / glyphs (the server bypasses `AllowableClass` for class 12, but the engine paints the line red and omits Paragon — the Lua hook recolors it green and adds the name so the player can tell it's wearable), and **PetFrame** re-anchored so the **pet unit frame sits below the rune row** for Paragon (stock layout had runes overlapping the pet portrait). The paper-doll **ammo slot** follows stock visibility rules (shown for hunters / ranged weapons; hidden when `UnitHasRelicSlot` applies). |
|
||||
| `patch-enUS-6.MPQ` | ~134 KB | The `ParagonAdvancement` addon. Replaces the talent pane (`N` key) for Paragon characters with the Character Advancement panel: per-class spell tabs, talent grid, Overview/Search tabs, AE/TE currency, commit / reset / preview, login-time toast suppression, a **PETS** tab with live hunter pet talent trees (preview learn, no TE/AE cost), a dedicated **Reset Pet Talents** control (server `PARAA` `C RESET PET TALENTS` — instant, no gold, no confirmation; requires matching worldserver), bottom-row **Reset all Abilities / Reset Build / Reset all Talents** disabled while on the PETS tab so those paths cannot dismiss the pet or unlearn Tame Beast, and a **Builds** page (full-pane overlay opened from the bottom-row Builds button) for saving named, icon-tagged loadouts: New Build (+) icon picker reuses `MACRO_ICON_FILENAMES`, right-click for edit/delete, shift-left-click to favorite (favorites bubble to the top), left-click pops a Load Build confirm. Build swaps reset + refund AE/TE, re-spend on the saved recipe, and **park hunter pets** to `PET_SAVE_NOT_IN_SLOT` so their name/talents/exp are preserved across swaps. |
|
||||
| `Wow.exe` | ~7.5 MB | 3.3.5a (build 12340) client byte-patched to skip the MPQ signature check so custom `patch-enUS-N.MPQ` files load. Diff against stock is a few bytes; everything else is unchanged. |
|
||||
|
||||
Server and client work as a pair: the addon talks to `mod-paragon` on the
|
||||
worldserver via `WHISPER` addon-channel messages with the `PARAA` prefix
|
||||
(currency push, spell/talent snapshot, commit, combo points, rune
|
||||
cooldowns, learn-toast silence window, **`C RESET PET TALENTS`**
|
||||
for hunter pet talent resets from the Character Advancement PETS tab,
|
||||
and the **build catalog** verbs `Q BUILDS` / `C BUILD NEW` / `C BUILD
|
||||
EDIT` / `C BUILD DELETE` / `C BUILD FAVORITE` / `C BUILD LOAD` for the
|
||||
saved-loadout system on the Builds page). Build swaps require the
|
||||
matching worldserver image because the swap path is server-driven
|
||||
(snapshot → reset → re-spend → pet park/unpark). Mismatched versions
|
||||
usually manifest as the panel rendering blank or AE/TE reading 0/0.
|
||||
|
||||
---
|
||||
|
||||
## Install
|
||||
|
||||
You need a 3.3.5a (build 12340) WoW client. ChromieCraft's free 3.3.5a
|
||||
download is the canonical clean source if you don't already have one.
|
||||
|
||||
1. Download every artifact from the latest release matching this server
|
||||
build:
|
||||
`https://github.com/Dawnforger/Fractured/releases/latest`
|
||||
2. Replace `Wow.exe` at the root of your client install with the patched
|
||||
one from the release. Keep a backup of the original if you want.
|
||||
3. Drop `patch-enUS-4.MPQ`, `patch-enUS-5.MPQ`, `patch-enUS-6.MPQ` into
|
||||
`Data/enUS/`. (For other locales, rename the suffix accordingly —
|
||||
contents are locale-independent, only the filename ordering matters
|
||||
to the loader.)
|
||||
4. Edit `Data/enUS/realmlist.wtf` to point at your server, e.g.
|
||||
`set realmlist your.host.tld`.
|
||||
5. Launch `Wow.exe`. The login screen should reach the realm list. Make
|
||||
a Paragon character (the new class entry on the create screen) and
|
||||
press `N` in-world to open the Character Advancement panel.
|
||||
|
||||
If the panel opens empty / AE+TE read 0/0 / `N` opens the stock talent
|
||||
pane: your client is loading an older `patch-enUS-6.MPQ`, or the
|
||||
worldserver image is older than commit `4d2a80d` (the
|
||||
`character_paragon_panel_spell_revoked` migration). Pull both ends to
|
||||
the same release tag and rebuild the worldserver image.
|
||||
|
||||
If the **client** shows the Paragon class on the create screen but the
|
||||
server replies **Character Creation Failed** (sometimes shown as
|
||||
"Error creating character") when you pick it -- **or** the character
|
||||
is created but spawns with no weapon / armor proficiencies (auto-attack
|
||||
greys out, can't equip anything beyond a fist), or with the proficiency
|
||||
**skills** but no **passive spells** like Block, Parry, Dual Wield --
|
||||
the worldserver is missing one of four pieces of class-12 data. All
|
||||
ship as SQL migrations under
|
||||
`modules/mod-paragon/data/sql/db-world/updates/` and are auto-applied
|
||||
by AzerothCore's DBUpdater on every `ac-db-import` run, but the SQL
|
||||
files are baked into the dbimport Docker image at build time -- so a
|
||||
stale image won't pick up new migrations. Fix:
|
||||
|
||||
```bash
|
||||
git pull origin main
|
||||
docker compose build ac-db-import ac-worldserver
|
||||
docker compose up -d ac-db-import
|
||||
docker compose restart ac-worldserver
|
||||
```
|
||||
|
||||
Existing class-12 characters created before these migrations will
|
||||
keep their broken state -- the cascade only fires inside
|
||||
`Player::Create` and `Player::LearnDefaultSkill` at character spawn.
|
||||
Delete the old Paragon and re-roll after the rebuild.
|
||||
|
||||
The four migrations:
|
||||
|
||||
- `2026_05_09_00.sql` -- DBC overlay rows for `chrclasses_dbc` and
|
||||
`skillraceclassinfo_dbc`. Without this the server can't even
|
||||
resolve class 12 in `sChrClassesStore`. See **Server-side Paragon
|
||||
DBC overlay** below.
|
||||
- `2026_05_10_00.sql` -- `playercreateinfo`, `playercreateinfo_action`,
|
||||
and `player_class_stats` rows for class 12. Without this
|
||||
`Player::Create` rejects every (race, class=12) pair as an
|
||||
"invalid race/class pair" and the worldserver prints
|
||||
`class-N Level-L does not have stats data!` integrity warnings on
|
||||
load.
|
||||
- `2026_05_10_01.sql` -- 20 `playercreateinfo_skills` rows
|
||||
(`classMask = 2048` = class 12) granting every weapon /
|
||||
armor proficiency at level 1. Without this a Paragon spawns with
|
||||
only the universal `classMask = 0` skills (Defense, Unarmed,
|
||||
Cloth, languages, Mounts) -- no Swords, no Mail, no Shield, etc.
|
||||
- `2026_05_10_02.sql` -- 3,314 `skilllineability_dbc` rows opening
|
||||
the class-12 bit on every SkillLineAbility row our patched
|
||||
`SkillLineAbility.dbc` modified. AC reads these rows in
|
||||
`Player::LearnDefaultSkill` to drive the `skill -> passive spell`
|
||||
cascade. Without it the proficiency *skills* from `_01.sql` exist
|
||||
but the *passive spells* (Block, Parry, Dual Wield, Defense,
|
||||
weapon Shoot, racial Mace/Sword Specialization, etc.) never auto-
|
||||
learn, so the spellbook past the racials looks empty.
|
||||
|
||||
After the rebuild + restart, `ac-worldserver` should log
|
||||
`>> Loaded 72 Player Create Definitions` (was 62 pre-Paragon),
|
||||
`>> Loaded 1391 Player Create Skills` (was 1371),
|
||||
`>> Loaded 10219 SkillLineAbility MultiMap Data` (unchanged total --
|
||||
the SQL overlay replaces existing rows by ID, doesn't add new ones),
|
||||
and character creation succeeds for any DK-eligible race with a full
|
||||
weapon / armor kit and the matching passive spells.
|
||||
|
||||
If the client **logs in** successfully but **disconnects immediately**
|
||||
when entering the realm: the auth server is handing your client the
|
||||
wrong world-server address. On a fresh local install the seed defaults
|
||||
to `127.0.0.1` (commit landing this paragraph). If your DB was
|
||||
imported from an older Fractured checkout, the seed may still point at
|
||||
`hsrwow.net`, which sends the client to our production world server
|
||||
instead of yours. Fix:
|
||||
|
||||
```bash
|
||||
# Docker:
|
||||
docker exec ac-database mysql -uroot -ppassword \
|
||||
-e "UPDATE acore_auth.realmlist SET address='127.0.0.1' WHERE id=1;"
|
||||
docker compose restart ac-authserver
|
||||
```
|
||||
|
||||
Substitute your public hostname/IP for `127.0.0.1` if remote players
|
||||
will be connecting. See `BUILD-NATIVE.md` -> *Production deployment
|
||||
overrides* for the full list of values to set on a production box.
|
||||
|
||||
---
|
||||
|
||||
## Server-side Paragon DBC overlay (automatic)
|
||||
|
||||
The Fractured **client** learns about Paragon from `patch-enUS-4.MPQ`
|
||||
(DBC + GlueXML). The **worldserver** never reads your MPQs — it reads
|
||||
plain `.dbc` files under its `DataDir` (`.../data/dbc/` by default).
|
||||
|
||||
Stock Docker installs populate `data/dbc/` from a vanilla 3.3.5a
|
||||
extract (`ac-client-data-init` in `docker-compose.yml`). That tree has
|
||||
no `ChrClasses` row for id **12** and no class-12 bit on
|
||||
`SkillRaceClassInfo` rows, which would normally trigger:
|
||||
|
||||
`Class (12) not found in DBC while creating new char ... wrong DBC files or cheater?`
|
||||
|
||||
…and reject the create with `CHAR_CREATE_FAILED`.
|
||||
|
||||
To remove that gap, the repo ships
|
||||
`modules/mod-paragon/data/sql/db-world/updates/2026_05_09_00.sql`,
|
||||
which `INSERT`s the Paragon class-12 deltas into:
|
||||
|
||||
- `chrclasses_dbc` — 1 row defining class 12 ("Paragon", power=Mana,
|
||||
family=Warrior, expansion=2).
|
||||
- `skillraceclassinfo_dbc` — 235 rows replacing stock entries with the
|
||||
patched ClassMask (class-12 bit OR'd in) so every baseline skill is
|
||||
available to Paragon characters.
|
||||
|
||||
`AzerothCore`'s DBC loader (`DBCStores.cpp::LoadDBC` -> `LoadFromDB`)
|
||||
merges these rows on top of whatever `data/dbc/` contains at every
|
||||
worldserver boot. The DBUpdater in `ac-db-import` (Docker) or the
|
||||
worldserver itself (native) applies the migration automatically — so
|
||||
the **only** steps a fresh contributor needs are `git clone` and
|
||||
`docker compose up -d`.
|
||||
|
||||
### Regenerating the migration
|
||||
|
||||
The SQL is auto-generated from the patched DBCs that already live
|
||||
inside `patch-enUS-4.MPQ`. The bake script lives outside this repo
|
||||
(per the repo-tidy policy) at:
|
||||
|
||||
`fractured-tooling/from-workspace-root/_gen_paragon_dbc_overlay_sql.py`
|
||||
|
||||
Re-run it whenever you change the Paragon DBC bake — for example,
|
||||
adding a new race to the Paragon class mask. It diffs the patched
|
||||
DBCs against a stock 3.3.5a DBC extract and emits a fresh
|
||||
`2026_05_09_00.sql` (or successor migration with a new timestamp if
|
||||
deltas change). Workflow:
|
||||
|
||||
```powershell
|
||||
# Extract the patched DBCs once:
|
||||
.\tools\mpq\mpqcli.exe extract `
|
||||
"ChromieCraft_3.3.5a\Data\enUS\patch-enUS-4.MPQ" `
|
||||
-o "$env:TEMP\paragon-dbc-extract"
|
||||
|
||||
# Regenerate the SQL migration:
|
||||
python fractured-tooling\from-workspace-root\_gen_paragon_dbc_overlay_sql.py
|
||||
```
|
||||
|
||||
If the regenerated SQL has new content, commit it as a **new** dated
|
||||
migration filename (e.g. `2026_06_01_00.sql`) — never edit a file that
|
||||
has already been applied to live databases, AC's DBUpdater will detect
|
||||
the hash change and re-run the SQL, which can be fine but is best
|
||||
reserved for emergencies.
|
||||
|
||||
### Manual DBC overlay (rare, fallback)
|
||||
|
||||
If you ever need the patched DBCs *on disk* — e.g. for a tool that
|
||||
reads `data/dbc/` directly outside the worldserver, or to verify a
|
||||
client-vs-server DBC mismatch — extract `patch-enUS-4.MPQ` and copy
|
||||
its `DBFilesClient/*.dbc` into `data/dbc/`:
|
||||
|
||||
**Docker:**
|
||||
|
||||
```powershell
|
||||
docker run --rm `
|
||||
-v ac-client-data:/data `
|
||||
-v ${PWD}\paragon-dbc-extract:/patch:ro `
|
||||
alpine sh -c "cp -f /patch/*.dbc /data/dbc/"
|
||||
docker compose restart ac-worldserver
|
||||
```
|
||||
|
||||
**Native:** copy into `<CMAKE_INSTALL_PREFIX>/data/dbc/` and restart.
|
||||
|
||||
This is **not required** for normal operation — the SQL migration
|
||||
covers everything `mod-paragon` needs at runtime. Use the manual
|
||||
overlay only when you're consciously bypassing the SQL merge layer.
|
||||
|
||||
---
|
||||
|
||||
## Building the patches yourself
|
||||
|
||||
The MPQs are reproducible from the dev tree (not in this repo —
|
||||
`Classless Dev/Paragon Patch UI/` and
|
||||
`Classless Dev/Paragon Advancement/` on the maintainer's workstation)
|
||||
via two PowerShell scripts in `tools/`:
|
||||
|
||||
```
|
||||
tools\build_paragon_ui_patch.ps1 -Deploy # -> patch-enUS-5.MPQ
|
||||
tools\build_paragon_advancement_patch.ps1 -Deploy # -> patch-enUS-6.MPQ
|
||||
```
|
||||
|
||||
`patch-enUS-4.MPQ` is the DBC + GlueXML bake; the bake scripts live with
|
||||
the rest of the dev tooling and are not part of this repo by design
|
||||
(see the repo-tidy policy in `README.txt` next to this file). Typical
|
||||
order on a maintainer machine:
|
||||
|
||||
1. `fractured-tooling/from-workspace-root/_patch_spell_dbc_runes.py` — stage `Spell.dbc` with `RuneCostID` cleared.
|
||||
2. `fractured-tooling/from-workspace-root/_patch_spell_dbc_reagents.py` — same staged `Spell.dbc`, clear class-spell reagents for client preflight.
|
||||
3. `fractured-tooling/from-workspace-root/_make_paragon_dbc_patch.py` — rebuild `ChrClasses` / `CharBaseInfo` / game tables, then pack `patch-enUS-4.MPQ`.
|
||||
|
||||
The patched `Wow.exe` is a one-time hex-edit of the stock 3.3.5a
|
||||
client. The diff is publicly documented in the WoW emulation community
|
||||
under names like "MPQ signature patch" / "no-CD-signature patch".
|
||||
|
||||
---
|
||||
|
||||
## Versioning
|
||||
|
||||
Releases are tagged to match the state of `main` they were built from.
|
||||
The release notes call out which server commit shipped alongside each
|
||||
artifact set, so a contributor running `git checkout <tag>` on this
|
||||
repo can pull the matching client bundle and have a guaranteed-aligned
|
||||
end-to-end build.
|
||||
@@ -0,0 +1,16 @@
|
||||
Fractured / Paragon — non-runtime repo extras
|
||||
==============================================
|
||||
|
||||
This folder holds material that is not required to configure, build, or run
|
||||
AzerothCore. Upstream AzerothCore does not ship these paths.
|
||||
|
||||
Contents:
|
||||
- BUILD-NATIVE.md — fork-specific native build notes (moved from repo root).
|
||||
- CLAUDE.md — optional AI assistant context (moved from repo root).
|
||||
- CLIENT-PATCHES.md — what ships in a Fractured client release (MPQs +
|
||||
patched Wow.exe), where to download them (Releases page), and how
|
||||
to install them. Binaries themselves are NOT in the tree.
|
||||
- *.log — local build logs (moved from repo root when present).
|
||||
|
||||
Operational files (docker-compose.override.yml, env/dist, modules/mod-paragon,
|
||||
etc.) stay at their normal locations.
|
||||
@@ -58,16 +58,16 @@ INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`,
|
||||
(90991, 90993, 4, 270, 515);
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (91247,91248,91249,90985,90986,90987,90988,90989,90990);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(91247, 91247, 3),
|
||||
(91247, 91248, 3),
|
||||
(91247, 91249, 3),
|
||||
(90985, 90985, 3),
|
||||
(90985, 90986, 3),
|
||||
(90985, 90987, 3),
|
||||
(90988, 90988, 3),
|
||||
(90988, 90989, 3),
|
||||
(90988, 90990, 3);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(91247, 91247, 0, 0, 3),
|
||||
(91247, 91248, 0, 0, 3),
|
||||
(91247, 91249, 0, 0, 3),
|
||||
(90985, 90985, 0, 0, 3),
|
||||
(90985, 90986, 0, 0, 3),
|
||||
(90985, 90987, 0, 0, 3),
|
||||
(90988, 90988, 0, 0, 3),
|
||||
(90988, 90989, 0, 0, 3),
|
||||
(90988, 90990, 0, 0, 3);
|
||||
|
||||
UPDATE `creature` SET `id1` = 15384 WHERE `guid` = 91250 AND `id1` = 19871;
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_
|
||||
(17653, 0, 3, 0, 4, 0, 100, 512, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 9, 17653, 0, 100, 1, 0, 0, 0, 0, 'Shadowmoon Channeler - On Aggro - Set In Combat With Zone');
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (@CGUID+0,@CGUID+1,@CGUID+2,@CGUID+3,@CGUID+4,@CGUID+5);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `groupAI`) VALUES
|
||||
(@CGUID+0, @CGUID+5, 24),
|
||||
(@CGUID+1, @CGUID+5, 24),
|
||||
(@CGUID+2, @CGUID+5, 24),
|
||||
(@CGUID+3, @CGUID+5, 24),
|
||||
(@CGUID+4, @CGUID+5, 24),
|
||||
(@CGUID+5, @CGUID+5, 24);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(@CGUID+0, @CGUID+5, 0, 0, 24),
|
||||
(@CGUID+1, @CGUID+5, 0, 0, 24),
|
||||
(@CGUID+2, @CGUID+5, 0, 0, 24),
|
||||
(@CGUID+3, @CGUID+5, 0, 0, 24),
|
||||
(@CGUID+4, @CGUID+5, 0, 0, 24),
|
||||
(@CGUID+5, @CGUID+5, 0, 0, 24);
|
||||
|
||||
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` IN (-138519,-138519,-138520,-138521,-138522,-138523));
|
||||
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
|
||||
|
||||
@@ -18,7 +18,7 @@ INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry
|
||||
(13, 1, 30952, 0, 0, 31, 0, 3, 17687, 0, 0, 0, 0, '', 'Shoot Flame Arrow (30952) only hit Flame Arrow (17687)');
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (151094,151095,151096,151097);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `groupAI`) VALUES
|
||||
(151095,151095,3),
|
||||
(151096,151095,3),
|
||||
(151097,151095,3);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(151095, 151095, 0, 0, 3),
|
||||
(151096, 151095, 0, 0, 3),
|
||||
(151097, 151095, 0, 0, 3);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
-- DB update 2023_07_10_00 -> 2023_07_10_01
|
||||
--
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (90978, 90979, 90980, 90981, 90982);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `groupAI`) VALUES
|
||||
(90978, 90978, 3),
|
||||
(90979, 90978, 3),
|
||||
(90980, 90978, 3),
|
||||
(90981, 90978, 3),
|
||||
(90982, 90978, 3);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(90978, 90978, 0, 0, 3),
|
||||
(90979, 90978, 0, 0, 3),
|
||||
(90980, 90978, 0, 0, 3),
|
||||
(90981, 90978, 0, 0, 3),
|
||||
(90982, 90978, 0, 0, 3);
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
-- DB update 2023_07_17_00 -> 2023_07_17_01
|
||||
--
|
||||
SET @attrmask_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'spell_enchant_proc_data'
|
||||
AND COLUMN_NAME = 'attributeMask'
|
||||
);
|
||||
SET @sql := IF(
|
||||
@attrmask_exists > 0,
|
||||
'SELECT 1',
|
||||
'ALTER TABLE `spell_enchant_proc_data` ADD COLUMN `attributeMask` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `procEx`'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
UPDATE `spell_enchant_proc_data` SET `attributeMask` = 0x2 WHERE `entry` = 2675;
|
||||
|
||||
@@ -3,7 +3,7 @@ UPDATE `creature_template` SET `scriptname` = '' WHERE `entry` = 27326;
|
||||
|
||||
DELETE FROM `spell_target_position` WHERE `ID` = 48324 AND `EffectIndex` = 0;
|
||||
INSERT INTO `spell_target_position` (`ID`, `EffectIndex`, `MapID`, `PositionX`, `PositionY`, `PositionZ`, `Orientation`, `VerifiedBuild`) VALUES
|
||||
(48324, 0, 571, 3454.11, -2802.37, 202.14, 0, 34149345);
|
||||
(48324, 0, 571, 3454.11, -2802.37, 202.14, 0, 0);
|
||||
|
||||
DELETE FROM `spell_script_names` WHERE `spell_id` IN (48382, 47533);
|
||||
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
-- DB update 2023_08_12_02 -> 2023_08_13_00
|
||||
--
|
||||
SET @csg_comment_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'creature_summon_groups'
|
||||
AND COLUMN_NAME = 'Comment'
|
||||
);
|
||||
SET @sql := IF(
|
||||
@csg_comment_exists > 0,
|
||||
'SELECT 1',
|
||||
'ALTER TABLE `creature_summon_groups` ADD COLUMN `Comment` varchar(255) NOT NULL DEFAULT '''' AFTER `summonTime`'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
DELETE FROM `event_scripts` WHERE `id` = 14376;
|
||||
|
||||
UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 185220;
|
||||
|
||||
@@ -1362,132 +1362,132 @@ UPDATE `creature_template_addon` SET `bytes2` = 1 WHERE (`entry` IN (21224, 2122
|
||||
UPDATE `creature_template` SET `speed_walk` = 1.6, `speed_run` = 1.71428 WHERE (`entry` IN (21218, 21301));
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (@CGUID+1 ,@CGUID+2 ,@CGUID+3 ,@CGUID+4 ,@CGUID+5 ,@CGUID+6 ,@CGUID+7 ,@CGUID+8 ,@CGUID+9 ,@CGUID+10 ,@CGUID+11 ,@CGUID+12 ,@CGUID+13 ,@CGUID+14 ,@CGUID+15 ,@CGUID+22 ,@CGUID+23 ,@CGUID+24 ,@CGUID+25 ,@CGUID+26 ,@CGUID+27 ,@CGUID+28 ,@CGUID+29 ,@CGUID+30 ,@CGUID+31 ,@CGUID+32 ,@CGUID+33 ,@CGUID+34 ,@CGUID+35 ,@CGUID+36 ,@CGUID+37 ,@CGUID+38 ,@CGUID+39 ,@CGUID+40 ,@CGUID+41 ,@CGUID+42 ,@CGUID+43 ,@CGUID+44 ,@CGUID+45 ,@CGUID+46 ,@CGUID+47 ,@CGUID+48 ,@CGUID+49 ,@CGUID+50 ,@CGUID+51 ,@CGUID+52 ,@CGUID+53 ,@CGUID+54 ,@CGUID+55 ,@CGUID+56 ,@CGUID+57 ,@CGUID+58 ,@CGUID+59 ,@CGUID+60 ,@CGUID+61 ,@CGUID+62 ,@CGUID+63 ,@CGUID+64 ,@CGUID+65 ,@CGUID+66 ,@CGUID+67 ,@CGUID+68 ,@CGUID+69 ,@CGUID+70 ,@CGUID+71 ,@CGUID+72 ,@CGUID+73 ,@CGUID+74 ,@CGUID+75 ,@CGUID+76 ,@CGUID+77 ,@CGUID+78 ,@CGUID+79 ,@CGUID+80 ,@CGUID+81 ,@CGUID+82 ,@CGUID+83 ,@CGUID+84 ,@CGUID+85 ,@CGUID+86 ,@CGUID+87 ,@CGUID+88 ,@CGUID+89 ,@CGUID+90 ,@CGUID+91 ,@CGUID+92 ,@CGUID+93 ,@CGUID+94 ,@CGUID+95 ,@CGUID+96 ,@CGUID+97 ,@CGUID+98 ,@CGUID+99 ,@CGUID+102,@CGUID+103,@CGUID+104,@CGUID+105,@CGUID+106,@CGUID+107,@CGUID+108,@CGUID+109,@CGUID+115,@CGUID+116,@CGUID+117,@CGUID+118,@CGUID+119,@CGUID+120,@CGUID+121,@CGUID+122,@CGUID+123,@CGUID+124,@CGUID+125,@CGUID+126,@CGUID+127,@CGUID+128,@CGUID+129,@CGUID+130,@CGUID+131,@CGUID+132,@CGUID+133,@CGUID+134,@CGUID+135,@CGUID+136,@CGUID+137,@CGUID+138);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `groupAI`) VALUES
|
||||
(@CGUID+1 , @CGUID+1 , 3),
|
||||
(@CGUID+2 , @CGUID+1 , 3),
|
||||
(@CGUID+3 , @CGUID+1 , 3),
|
||||
(@CGUID+4 , @CGUID+1 , 3),
|
||||
(@CGUID+5 , @CGUID+1 , 3),
|
||||
(@CGUID+6 , @CGUID+6 , 3),
|
||||
(@CGUID+7 , @CGUID+6 , 3),
|
||||
(@CGUID+8 , @CGUID+6 , 3),
|
||||
(@CGUID+9 , @CGUID+6 , 3),
|
||||
(@CGUID+10 , @CGUID+6 , 3),
|
||||
(@CGUID+11 , @CGUID+11 , 3),
|
||||
(@CGUID+12 , @CGUID+11 , 3),
|
||||
(@CGUID+13 , @CGUID+11 , 3),
|
||||
(@CGUID+14 , @CGUID+11 , 3),
|
||||
(@CGUID+15 , @CGUID+11 , 3),
|
||||
(@CGUID+22 , @CGUID+22 , 3),
|
||||
(@CGUID+23 , @CGUID+22 , 3),
|
||||
(@CGUID+24 , @CGUID+22 , 3),
|
||||
(@CGUID+25 , @CGUID+22 , 3),
|
||||
(@CGUID+26 , @CGUID+22 , 3),
|
||||
(@CGUID+27 , @CGUID+22 , 3),
|
||||
(@CGUID+28 , @CGUID+22 , 3),
|
||||
(@CGUID+29 , @CGUID+22 , 3),
|
||||
(@CGUID+30 , @CGUID+22 , 3),
|
||||
(@CGUID+31 , @CGUID+31 , 3),
|
||||
(@CGUID+32 , @CGUID+31 , 3),
|
||||
(@CGUID+33 , @CGUID+31 , 3),
|
||||
(@CGUID+34 , @CGUID+31 , 3),
|
||||
(@CGUID+35 , @CGUID+31 , 3),
|
||||
(@CGUID+36 , @CGUID+31 , 3),
|
||||
(@CGUID+37 , @CGUID+31 , 3),
|
||||
(@CGUID+38 , @CGUID+31 , 3),
|
||||
(@CGUID+39 , @CGUID+31 , 3),
|
||||
(@CGUID+40 , @CGUID+40 , 3),
|
||||
(@CGUID+41 , @CGUID+40 , 3),
|
||||
(@CGUID+42 , @CGUID+40 , 3),
|
||||
(@CGUID+43 , @CGUID+40 , 3),
|
||||
(@CGUID+44 , @CGUID+40 , 3),
|
||||
(@CGUID+45 , @CGUID+40 , 3),
|
||||
(@CGUID+46 , @CGUID+40 , 3),
|
||||
(@CGUID+47 , @CGUID+40 , 3),
|
||||
(@CGUID+48 , @CGUID+40 , 3),
|
||||
(@CGUID+49 , @CGUID+49 , 3),
|
||||
(@CGUID+50 , @CGUID+49 , 3),
|
||||
(@CGUID+51 , @CGUID+49 , 3),
|
||||
(@CGUID+52 , @CGUID+49 , 3),
|
||||
(@CGUID+53 , @CGUID+49 , 3),
|
||||
(@CGUID+54 , @CGUID+49 , 3),
|
||||
(@CGUID+55 , @CGUID+49 , 3),
|
||||
(@CGUID+56 , @CGUID+49 , 3),
|
||||
(@CGUID+57 , @CGUID+49 , 3),
|
||||
(@CGUID+58 , @CGUID+58 , 3),
|
||||
(@CGUID+59 , @CGUID+58 , 3),
|
||||
(@CGUID+60 , @CGUID+58 , 3),
|
||||
(@CGUID+61 , @CGUID+58 , 3),
|
||||
(@CGUID+62 , @CGUID+58 , 3),
|
||||
(@CGUID+63 , @CGUID+58 , 3),
|
||||
(@CGUID+64 , @CGUID+58 , 3),
|
||||
(@CGUID+65 , @CGUID+58 , 3),
|
||||
(@CGUID+66 , @CGUID+58 , 3),
|
||||
(@CGUID+67 , @CGUID+67 , 3),
|
||||
(@CGUID+68 , @CGUID+67 , 3),
|
||||
(@CGUID+69 , @CGUID+67 , 3),
|
||||
(@CGUID+70 , @CGUID+67 , 3),
|
||||
(@CGUID+71 , @CGUID+67 , 3),
|
||||
(@CGUID+72 , @CGUID+67 , 3),
|
||||
(@CGUID+73 , @CGUID+67 , 3),
|
||||
(@CGUID+74 , @CGUID+67 , 3),
|
||||
(@CGUID+75 , @CGUID+67 , 3),
|
||||
(@CGUID+76 , @CGUID+76 , 3),
|
||||
(@CGUID+77 , @CGUID+76 , 3),
|
||||
(@CGUID+78 , @CGUID+76 , 3),
|
||||
(@CGUID+79 , @CGUID+76 , 3),
|
||||
(@CGUID+80 , @CGUID+76 , 3),
|
||||
(@CGUID+81 , @CGUID+76 , 3),
|
||||
(@CGUID+82 , @CGUID+82 , 3),
|
||||
(@CGUID+83 , @CGUID+82 , 3),
|
||||
(@CGUID+84 , @CGUID+82 , 3),
|
||||
(@CGUID+85 , @CGUID+82 , 3),
|
||||
(@CGUID+86 , @CGUID+82 , 3),
|
||||
(@CGUID+87 , @CGUID+82 , 3),
|
||||
(@CGUID+88 , @CGUID+88 , 3),
|
||||
(@CGUID+89 , @CGUID+88 , 3),
|
||||
(@CGUID+90 , @CGUID+88 , 3),
|
||||
(@CGUID+91 , @CGUID+88 , 3),
|
||||
(@CGUID+92 , @CGUID+88 , 3),
|
||||
(@CGUID+93 , @CGUID+88 , 3),
|
||||
(@CGUID+94 , @CGUID+94 , 3),
|
||||
(@CGUID+95 , @CGUID+94 , 3),
|
||||
(@CGUID+96 , @CGUID+94 , 3),
|
||||
(@CGUID+97 , @CGUID+94 , 3),
|
||||
(@CGUID+98 , @CGUID+94 , 3),
|
||||
(@CGUID+99 , @CGUID+94 , 3),
|
||||
(@CGUID+102, @CGUID+102, 3),
|
||||
(@CGUID+103, @CGUID+102, 3),
|
||||
(@CGUID+104, @CGUID+102, 3),
|
||||
(@CGUID+105, @CGUID+102, 3),
|
||||
(@CGUID+106, @CGUID+106, 3),
|
||||
(@CGUID+107, @CGUID+106, 3),
|
||||
(@CGUID+108, @CGUID+106, 3),
|
||||
(@CGUID+109, @CGUID+106, 3),
|
||||
(@CGUID+115, @CGUID+115, 3),
|
||||
(@CGUID+116, @CGUID+115, 3),
|
||||
(@CGUID+117, @CGUID+115, 3),
|
||||
(@CGUID+118, @CGUID+118, 3),
|
||||
(@CGUID+119, @CGUID+118, 3),
|
||||
(@CGUID+120, @CGUID+118, 3),
|
||||
(@CGUID+121, @CGUID+118, 3),
|
||||
(@CGUID+122, @CGUID+122, 3),
|
||||
(@CGUID+123, @CGUID+122, 3),
|
||||
(@CGUID+124, @CGUID+122, 3),
|
||||
(@CGUID+125, @CGUID+122, 3),
|
||||
(@CGUID+126, @CGUID+122, 3),
|
||||
(@CGUID+127, @CGUID+127, 3),
|
||||
(@CGUID+128, @CGUID+127, 3),
|
||||
(@CGUID+129, @CGUID+127, 3),
|
||||
(@CGUID+130, @CGUID+127, 3),
|
||||
(@CGUID+131, @CGUID+127, 3),
|
||||
(@CGUID+132, @CGUID+127, 3),
|
||||
(@CGUID+133, @CGUID+133, 3),
|
||||
(@CGUID+134, @CGUID+133, 3),
|
||||
(@CGUID+135, @CGUID+133, 3),
|
||||
(@CGUID+136, @CGUID+133, 3),
|
||||
(@CGUID+137, @CGUID+133, 3),
|
||||
(@CGUID+138, @CGUID+133, 3);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(@CGUID+1, @CGUID+1, 0, 0, 3),
|
||||
(@CGUID+2, @CGUID+1, 0, 0, 3),
|
||||
(@CGUID+3, @CGUID+1, 0, 0, 3),
|
||||
(@CGUID+4, @CGUID+1, 0, 0, 3),
|
||||
(@CGUID+5, @CGUID+1, 0, 0, 3),
|
||||
(@CGUID+6, @CGUID+6, 0, 0, 3),
|
||||
(@CGUID+7, @CGUID+6, 0, 0, 3),
|
||||
(@CGUID+8, @CGUID+6, 0, 0, 3),
|
||||
(@CGUID+9, @CGUID+6, 0, 0, 3),
|
||||
(@CGUID+10, @CGUID+6, 0, 0, 3),
|
||||
(@CGUID+11, @CGUID+11, 0, 0, 3),
|
||||
(@CGUID+12, @CGUID+11, 0, 0, 3),
|
||||
(@CGUID+13, @CGUID+11, 0, 0, 3),
|
||||
(@CGUID+14, @CGUID+11, 0, 0, 3),
|
||||
(@CGUID+15, @CGUID+11, 0, 0, 3),
|
||||
(@CGUID+22, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+23, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+24, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+25, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+27, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+28, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+29, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+30, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+31, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+32, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+33, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+34, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+35, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+36, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+37, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+38, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+39, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+40, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+41, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+42, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+43, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+44, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+45, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+46, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+47, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+48, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+49, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+50, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+51, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+52, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+53, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+54, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+55, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+56, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+57, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+58, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+59, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+60, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+61, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+62, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+63, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+64, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+65, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+66, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+67, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+68, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+69, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+70, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+71, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+72, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+73, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+74, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+75, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+76, @CGUID+76, 0, 0, 3),
|
||||
(@CGUID+77, @CGUID+76, 0, 0, 3),
|
||||
(@CGUID+78, @CGUID+76, 0, 0, 3),
|
||||
(@CGUID+79, @CGUID+76, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+76, 0, 0, 3),
|
||||
(@CGUID+81, @CGUID+76, 0, 0, 3),
|
||||
(@CGUID+82, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+83, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+84, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+85, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+86, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+87, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+88, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+89, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+90, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+91, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+92, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+93, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+94, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+95, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+96, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+97, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+98, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+99, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+102, @CGUID+102, 0, 0, 3),
|
||||
(@CGUID+103, @CGUID+102, 0, 0, 3),
|
||||
(@CGUID+104, @CGUID+102, 0, 0, 3),
|
||||
(@CGUID+105, @CGUID+102, 0, 0, 3),
|
||||
(@CGUID+106, @CGUID+106, 0, 0, 3),
|
||||
(@CGUID+107, @CGUID+106, 0, 0, 3),
|
||||
(@CGUID+108, @CGUID+106, 0, 0, 3),
|
||||
(@CGUID+109, @CGUID+106, 0, 0, 3),
|
||||
(@CGUID+115, @CGUID+115, 0, 0, 3),
|
||||
(@CGUID+116, @CGUID+115, 0, 0, 3),
|
||||
(@CGUID+117, @CGUID+115, 0, 0, 3),
|
||||
(@CGUID+118, @CGUID+118, 0, 0, 3),
|
||||
(@CGUID+119, @CGUID+118, 0, 0, 3),
|
||||
(@CGUID+120, @CGUID+118, 0, 0, 3),
|
||||
(@CGUID+121, @CGUID+118, 0, 0, 3),
|
||||
(@CGUID+122, @CGUID+122, 0, 0, 3),
|
||||
(@CGUID+123, @CGUID+122, 0, 0, 3),
|
||||
(@CGUID+124, @CGUID+122, 0, 0, 3),
|
||||
(@CGUID+125, @CGUID+122, 0, 0, 3),
|
||||
(@CGUID+126, @CGUID+122, 0, 0, 3),
|
||||
(@CGUID+127, @CGUID+127, 0, 0, 3),
|
||||
(@CGUID+128, @CGUID+127, 0, 0, 3),
|
||||
(@CGUID+129, @CGUID+127, 0, 0, 3),
|
||||
(@CGUID+130, @CGUID+127, 0, 0, 3),
|
||||
(@CGUID+131, @CGUID+127, 0, 0, 3),
|
||||
(@CGUID+132, @CGUID+127, 0, 0, 3),
|
||||
(@CGUID+133, @CGUID+133, 0, 0, 3),
|
||||
(@CGUID+134, @CGUID+133, 0, 0, 3),
|
||||
(@CGUID+135, @CGUID+133, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+133, 0, 0, 3),
|
||||
(@CGUID+137, @CGUID+133, 0, 0, 3),
|
||||
(@CGUID+138, @CGUID+133, 0, 0, 3);
|
||||
|
||||
UPDATE `smart_scripts` SET `action_param2`=0 WHERE `entryorguid`=21230 AND `source_type`=0 AND `id`=1 AND `link`=0;
|
||||
UPDATE `smart_scripts` SET `action_param2`=0 WHERE `entryorguid`=21230 AND `source_type`=0 AND `id`=5 AND `link`=0;
|
||||
|
||||
@@ -183,11 +183,11 @@ INSERT INTO `linked_respawn`(`guid`, `linkedGuid`, `linkType`) VALUES
|
||||
|
||||
-- Leotheras formation
|
||||
DELETE FROM `creature_formations` WHERE `leaderGUID` = @LEOTHERAS;
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(@LEOTHERAS, @LEOTHERAS, 24),
|
||||
(@LEOTHERAS, @LEOTHERAS+1, 24),
|
||||
(@LEOTHERAS, @LEOTHERAS+2, 24),
|
||||
(@LEOTHERAS, @LEOTHERAS+3, 24);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(@LEOTHERAS, @LEOTHERAS, 0, 0, 24),
|
||||
(@LEOTHERAS, @LEOTHERAS+1, 0, 0, 24),
|
||||
(@LEOTHERAS, @LEOTHERAS+2, 0, 0, 24),
|
||||
(@LEOTHERAS, @LEOTHERAS+3, 0, 0, 24);
|
||||
|
||||
SET @KARATHRESS := 153154;
|
||||
DELETE FROM `linked_respawn` WHERE `linkedGuid` = @KARATHRESS;
|
||||
|
||||
@@ -869,176 +869,176 @@ INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_
|
||||
|
||||
-- Static Formations
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (@CGUID+03,@CGUID+04,@CGUID+05,@CGUID+06,@CGUID+07,@CGUID+08,@CGUID+09,@CGUID+10,@CGUID+11,@CGUID+12,@CGUID+13,@CGUID+14,@CGUID+15,@CGUID+16,@CGUID+17,@CGUID+18,@CGUID+19,@CGUID+20,@CGUID+52,@CGUID+53,@CGUID+54,@CGUID+55,@CGUID+59,@CGUID+60,@CGUID+61,@CGUID+62,@CGUID+63,@CGUID+64,@CGUID+65,@CGUID+66,@CGUID+67,@CGUID+68,@CGUID+69,@CGUID+70,@CGUID+74,@CGUID+75,@CGUID+76,@CGUID+77,@CGUID+78,@CGUID+79,@CGUID+80,@CGUID+81,@CGUID+82,@CGUID+83,@CGUID+84,@CGUID+85,@CGUID+86,@CGUID+87,@CGUID+88,@CGUID+89,@CGUID+90,@CGUID+91,@CGUID+93,@CGUID+94,@CGUID+98,@CGUID+99,@CGUID+100,@CGUID+101,@CGUID+102,@CGUID+103,@CGUID+104,@CGUID+105,@CGUID+106,@CGUID+107,@CGUID+108,@CGUID+109,@CGUID+110,@CGUID+111,@CGUID+112,@CGUID+113,@CGUID+114,@CGUID+115,@CGUID+116,@CGUID+117,@CGUID+118,@CGUID+119,@CGUID+120,@CGUID+121,@CGUID+125,@CGUID+126,@CGUID+127,@CGUID+128,@CGUID+129,@CGUID+130,@CGUID+131,@CGUID+132,@CGUID+133,@CGUID+134,@CGUID+135,@CGUID+136,@CGUID+143,@CGUID+144,@CGUID+145,@CGUID+146,@CGUID+147,@CGUID+148,@CGUID+149,@CGUID+150,@CGUID+151,@CGUID+152,@CGUID+153,@CGUID+154,@CGUID+155,@CGUID+156,@CGUID+157,@CGUID+158,@CGUID+159,@CGUID+160,@CGUID+161,@CGUID+162,@CGUID+163,@CGUID+164,@CGUID+165,@CGUID+166,@CGUID+167,@CGUID+168,@CGUID+169,@CGUID+170,@CGUID+171,@CGUID+172,@CGUID+173,@CGUID+174,@CGUID+175,@CGUID+176,@CGUID+177,@CGUID+178,@CGUID+180,@CGUID+181,@CGUID+182,@CGUID+183,@CGUID+184,@CGUID+185,@CGUID+186,@CGUID+187,@CGUID+188,@CGUID+189,@CGUID+190,@CGUID+191,@CGUID+218,@CGUID+219,@CGUID+220,@CGUID+221,@CGUID+222) AND `groupAI` IN (3, 24);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `groupAI`) VALUES
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
-- Entrance Group 1
|
||||
(@CGUID+3, @CGUID+3, 3),
|
||||
(@CGUID+4, @CGUID+3, 3),
|
||||
(@CGUID+5, @CGUID+3, 3),
|
||||
(@CGUID+6, @CGUID+3, 3),
|
||||
(@CGUID+7, @CGUID+3, 3),
|
||||
(@CGUID+8, @CGUID+3, 3),
|
||||
(@CGUID+3, @CGUID+3, 0, 0, 3),
|
||||
(@CGUID+4, @CGUID+3, 0, 0, 3),
|
||||
(@CGUID+5, @CGUID+3, 0, 0, 3),
|
||||
(@CGUID+6, @CGUID+3, 0, 0, 3),
|
||||
(@CGUID+7, @CGUID+3, 0, 0, 3),
|
||||
(@CGUID+8, @CGUID+3, 0, 0, 3),
|
||||
-- Entrance Group 2
|
||||
(@CGUID+9 , @CGUID+9, 3),
|
||||
(@CGUID+10, @CGUID+9, 3),
|
||||
(@CGUID+11, @CGUID+9, 3),
|
||||
(@CGUID+12, @CGUID+9, 3),
|
||||
(@CGUID+13, @CGUID+9, 3),
|
||||
(@CGUID+14, @CGUID+9, 3),
|
||||
(@CGUID+9, @CGUID+9, 0, 0, 3),
|
||||
(@CGUID+10, @CGUID+9, 0, 0, 3),
|
||||
(@CGUID+11, @CGUID+9, 0, 0, 3),
|
||||
(@CGUID+12, @CGUID+9, 0, 0, 3),
|
||||
(@CGUID+13, @CGUID+9, 0, 0, 3),
|
||||
(@CGUID+14, @CGUID+9, 0, 0, 3),
|
||||
-- Entrance Group 3
|
||||
(@CGUID+15, @CGUID+15, 3),
|
||||
(@CGUID+16, @CGUID+15, 3),
|
||||
(@CGUID+17, @CGUID+15, 3),
|
||||
(@CGUID+18, @CGUID+15, 3),
|
||||
(@CGUID+19, @CGUID+15, 3),
|
||||
(@CGUID+20, @CGUID+15, 3),
|
||||
(@CGUID+15, @CGUID+15, 0, 0, 3),
|
||||
(@CGUID+16, @CGUID+15, 0, 0, 3),
|
||||
(@CGUID+17, @CGUID+15, 0, 0, 3),
|
||||
(@CGUID+18, @CGUID+15, 0, 0, 3),
|
||||
(@CGUID+19, @CGUID+15, 0, 0, 3),
|
||||
(@CGUID+20, @CGUID+15, 0, 0, 3),
|
||||
-- Sentinel Group 1
|
||||
(@CGUID+52, @CGUID+52, 3),
|
||||
(@CGUID+53, @CGUID+52, 3),
|
||||
(@CGUID+52, @CGUID+52, 0, 0, 3),
|
||||
(@CGUID+53, @CGUID+52, 0, 0, 3),
|
||||
-- Sentinel Group 2
|
||||
(@CGUID+54, @CGUID+54, 3),
|
||||
(@CGUID+55, @CGUID+54, 3),
|
||||
(@CGUID+54, @CGUID+54, 0, 0, 3),
|
||||
(@CGUID+55, @CGUID+54, 0, 0, 3),
|
||||
-- Inquisitor Adds 1
|
||||
(@CGUID+59, @CGUID+59, 3),
|
||||
(@CGUID+60, @CGUID+59, 3),
|
||||
(@CGUID+61, @CGUID+59, 3),
|
||||
(@CGUID+62, @CGUID+59, 3),
|
||||
(@CGUID+63, @CGUID+59, 3),
|
||||
(@CGUID+64, @CGUID+59, 3),
|
||||
(@CGUID+59, @CGUID+59, 0, 0, 3),
|
||||
(@CGUID+60, @CGUID+59, 0, 0, 3),
|
||||
(@CGUID+61, @CGUID+59, 0, 0, 3),
|
||||
(@CGUID+62, @CGUID+59, 0, 0, 3),
|
||||
(@CGUID+63, @CGUID+59, 0, 0, 3),
|
||||
(@CGUID+64, @CGUID+59, 0, 0, 3),
|
||||
-- Inquisitor Adds 2
|
||||
(@CGUID+65, @CGUID+65, 3),
|
||||
(@CGUID+66, @CGUID+65, 3),
|
||||
(@CGUID+67, @CGUID+65, 3),
|
||||
(@CGUID+68, @CGUID+65, 3),
|
||||
(@CGUID+69, @CGUID+65, 3),
|
||||
(@CGUID+70, @CGUID+65, 3),
|
||||
(@CGUID+65, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+66, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+67, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+68, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+69, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+70, @CGUID+65, 0, 0, 3),
|
||||
-- Void Reaver Trash Group 1
|
||||
(@CGUID+74, @CGUID+74, 3),
|
||||
(@CGUID+75, @CGUID+74, 3),
|
||||
(@CGUID+76, @CGUID+74, 3),
|
||||
(@CGUID+77, @CGUID+74, 3),
|
||||
(@CGUID+78, @CGUID+74, 3),
|
||||
(@CGUID+74, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+75, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+76, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+77, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+78, @CGUID+74, 0, 0, 3),
|
||||
-- Void Reaver Trash Group 2
|
||||
(@CGUID+79, @CGUID+79, 3),
|
||||
(@CGUID+80, @CGUID+79, 3),
|
||||
(@CGUID+81, @CGUID+79, 3),
|
||||
(@CGUID+82, @CGUID+79, 3),
|
||||
(@CGUID+83, @CGUID+79, 3),
|
||||
(@CGUID+79, @CGUID+79, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+79, 0, 0, 3),
|
||||
(@CGUID+81, @CGUID+79, 0, 0, 3),
|
||||
(@CGUID+82, @CGUID+79, 0, 0, 3),
|
||||
(@CGUID+83, @CGUID+79, 0, 0, 3),
|
||||
-- Void Reaver Trash Group 3
|
||||
(@CGUID+84, @CGUID+84, 3),
|
||||
(@CGUID+85, @CGUID+84, 3),
|
||||
(@CGUID+86, @CGUID+84, 3),
|
||||
(@CGUID+87, @CGUID+84, 3),
|
||||
(@CGUID+84, @CGUID+84, 0, 0, 3),
|
||||
(@CGUID+85, @CGUID+84, 0, 0, 3),
|
||||
(@CGUID+86, @CGUID+84, 0, 0, 3),
|
||||
(@CGUID+87, @CGUID+84, 0, 0, 3),
|
||||
-- Void Reaver Trash Group 4
|
||||
(@CGUID+88, @CGUID+88, 3),
|
||||
(@CGUID+89, @CGUID+88, 3),
|
||||
(@CGUID+90, @CGUID+88, 3),
|
||||
(@CGUID+91, @CGUID+88, 3),
|
||||
(@CGUID+88, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+89, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+90, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+91, @CGUID+88, 0, 0, 3),
|
||||
-- Solarium Sentinel Group 1
|
||||
(@CGUID+93, @CGUID+93, 3),
|
||||
(@CGUID+94, @CGUID+93, 3),
|
||||
(@CGUID+93, @CGUID+93, 0, 0, 3),
|
||||
(@CGUID+94, @CGUID+93, 0, 0, 3),
|
||||
-- Solarium Large Group 1
|
||||
(@CGUID+98 , @CGUID+98, 3),
|
||||
(@CGUID+99 , @CGUID+98, 3),
|
||||
(@CGUID+100, @CGUID+98, 3),
|
||||
(@CGUID+101, @CGUID+98, 3),
|
||||
(@CGUID+102, @CGUID+98, 3),
|
||||
(@CGUID+103, @CGUID+98, 3),
|
||||
(@CGUID+104, @CGUID+98, 3),
|
||||
(@CGUID+105, @CGUID+98, 3),
|
||||
(@CGUID+106, @CGUID+98, 3),
|
||||
(@CGUID+107, @CGUID+98, 3),
|
||||
(@CGUID+108, @CGUID+98, 3),
|
||||
(@CGUID+109, @CGUID+98, 3),
|
||||
(@CGUID+98, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+99, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+100, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+101, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+102, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+103, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+104, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+105, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+106, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+107, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+108, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+109, @CGUID+98, 0, 0, 3),
|
||||
-- Solarium Large Group 2
|
||||
(@CGUID+110, @CGUID+110, 3),
|
||||
(@CGUID+111, @CGUID+110, 3),
|
||||
(@CGUID+112, @CGUID+110, 3),
|
||||
(@CGUID+113, @CGUID+110, 3),
|
||||
(@CGUID+114, @CGUID+110, 3),
|
||||
(@CGUID+115, @CGUID+110, 3),
|
||||
(@CGUID+116, @CGUID+110, 3),
|
||||
(@CGUID+117, @CGUID+110, 3),
|
||||
(@CGUID+118, @CGUID+110, 3),
|
||||
(@CGUID+119, @CGUID+110, 3),
|
||||
(@CGUID+120, @CGUID+110, 3),
|
||||
(@CGUID+121, @CGUID+110, 3),
|
||||
(@CGUID+110, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+111, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+112, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+113, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+114, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+115, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+116, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+117, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+118, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+119, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+120, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+121, @CGUID+110, 0, 0, 3),
|
||||
-- Inquisitor Adds 3
|
||||
(@CGUID+125, @CGUID+125, 3),
|
||||
(@CGUID+126, @CGUID+125, 3),
|
||||
(@CGUID+127, @CGUID+125, 3),
|
||||
(@CGUID+128, @CGUID+125, 3),
|
||||
(@CGUID+129, @CGUID+125, 3),
|
||||
(@CGUID+130, @CGUID+125, 3),
|
||||
(@CGUID+125, @CGUID+125, 0, 0, 3),
|
||||
(@CGUID+126, @CGUID+125, 0, 0, 3),
|
||||
(@CGUID+127, @CGUID+125, 0, 0, 3),
|
||||
(@CGUID+128, @CGUID+125, 0, 0, 3),
|
||||
(@CGUID+129, @CGUID+125, 0, 0, 3),
|
||||
(@CGUID+130, @CGUID+125, 0, 0, 3),
|
||||
-- Inquisitor Adds 4
|
||||
(@CGUID+131, @CGUID+131, 3),
|
||||
(@CGUID+132, @CGUID+131, 3),
|
||||
(@CGUID+133, @CGUID+131, 3),
|
||||
(@CGUID+134, @CGUID+131, 3),
|
||||
(@CGUID+135, @CGUID+131, 3),
|
||||
(@CGUID+136, @CGUID+131, 3),
|
||||
(@CGUID+131, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+132, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+133, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+134, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+135, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+131, 0, 0, 3),
|
||||
-- Solarian Adds 1
|
||||
(@CGUID+143, @CGUID+143, 3),
|
||||
(@CGUID+144, @CGUID+143, 3),
|
||||
(@CGUID+145, @CGUID+143, 3),
|
||||
(@CGUID+146, @CGUID+143, 3),
|
||||
(@CGUID+147, @CGUID+143, 3),
|
||||
(@CGUID+148, @CGUID+143, 3),
|
||||
(@CGUID+143, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+144, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+145, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+146, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+147, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+148, @CGUID+143, 0, 0, 3),
|
||||
-- Solarian Adds 2
|
||||
(@CGUID+149, @CGUID+149, 3),
|
||||
(@CGUID+150, @CGUID+149, 3),
|
||||
(@CGUID+151, @CGUID+149, 3),
|
||||
(@CGUID+152, @CGUID+149, 3),
|
||||
(@CGUID+153, @CGUID+149, 3),
|
||||
(@CGUID+154, @CGUID+149, 3),
|
||||
(@CGUID+149, @CGUID+149, 0, 0, 3),
|
||||
(@CGUID+150, @CGUID+149, 0, 0, 3),
|
||||
(@CGUID+151, @CGUID+149, 0, 0, 3),
|
||||
(@CGUID+152, @CGUID+149, 0, 0, 3),
|
||||
(@CGUID+153, @CGUID+149, 0, 0, 3),
|
||||
(@CGUID+154, @CGUID+149, 0, 0, 3),
|
||||
-- Solarian Adds 3
|
||||
(@CGUID+155, @CGUID+155, 3),
|
||||
(@CGUID+156, @CGUID+155, 3),
|
||||
(@CGUID+157, @CGUID+155, 3),
|
||||
(@CGUID+158, @CGUID+155, 3),
|
||||
(@CGUID+159, @CGUID+155, 3),
|
||||
(@CGUID+160, @CGUID+155, 3),
|
||||
(@CGUID+155, @CGUID+155, 0, 0, 3),
|
||||
(@CGUID+156, @CGUID+155, 0, 0, 3),
|
||||
(@CGUID+157, @CGUID+155, 0, 0, 3),
|
||||
(@CGUID+158, @CGUID+155, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+155, 0, 0, 3),
|
||||
(@CGUID+160, @CGUID+155, 0, 0, 3),
|
||||
-- Solarian Adds 4
|
||||
(@CGUID+161, @CGUID+161, 3),
|
||||
(@CGUID+162, @CGUID+161, 3),
|
||||
(@CGUID+163, @CGUID+161, 3),
|
||||
(@CGUID+164, @CGUID+161, 3),
|
||||
(@CGUID+165, @CGUID+161, 3),
|
||||
(@CGUID+166, @CGUID+161, 3),
|
||||
(@CGUID+161, @CGUID+161, 0, 0, 3),
|
||||
(@CGUID+162, @CGUID+161, 0, 0, 3),
|
||||
(@CGUID+163, @CGUID+161, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+161, 0, 0, 3),
|
||||
(@CGUID+165, @CGUID+161, 0, 0, 3),
|
||||
(@CGUID+166, @CGUID+161, 0, 0, 3),
|
||||
-- Solarian Adds 5
|
||||
(@CGUID+167, @CGUID+167, 3),
|
||||
(@CGUID+168, @CGUID+167, 3),
|
||||
(@CGUID+169, @CGUID+167, 3),
|
||||
(@CGUID+170, @CGUID+167, 3),
|
||||
(@CGUID+171, @CGUID+167, 3),
|
||||
(@CGUID+172, @CGUID+167, 3),
|
||||
(@CGUID+167, @CGUID+167, 0, 0, 3),
|
||||
(@CGUID+168, @CGUID+167, 0, 0, 3),
|
||||
(@CGUID+169, @CGUID+167, 0, 0, 3),
|
||||
(@CGUID+170, @CGUID+167, 0, 0, 3),
|
||||
(@CGUID+171, @CGUID+167, 0, 0, 3),
|
||||
(@CGUID+172, @CGUID+167, 0, 0, 3),
|
||||
-- Solarian Adds 6
|
||||
(@CGUID+173, @CGUID+173, 3),
|
||||
(@CGUID+174, @CGUID+173, 3),
|
||||
(@CGUID+175, @CGUID+173, 3),
|
||||
(@CGUID+176, @CGUID+173, 3),
|
||||
(@CGUID+177, @CGUID+173, 3),
|
||||
(@CGUID+178, @CGUID+173, 3),
|
||||
(@CGUID+173, @CGUID+173, 0, 0, 3),
|
||||
(@CGUID+174, @CGUID+173, 0, 0, 3),
|
||||
(@CGUID+175, @CGUID+173, 0, 0, 3),
|
||||
(@CGUID+176, @CGUID+173, 0, 0, 3),
|
||||
(@CGUID+177, @CGUID+173, 0, 0, 3),
|
||||
(@CGUID+178, @CGUID+173, 0, 0, 3),
|
||||
-- Kael Trash 1
|
||||
(@CGUID+180, @CGUID+180, 3),
|
||||
(@CGUID+181, @CGUID+180, 3),
|
||||
(@CGUID+182, @CGUID+180, 3),
|
||||
(@CGUID+183, @CGUID+180, 3),
|
||||
(@CGUID+180, @CGUID+180, 0, 0, 3),
|
||||
(@CGUID+181, @CGUID+180, 0, 0, 3),
|
||||
(@CGUID+182, @CGUID+180, 0, 0, 3),
|
||||
(@CGUID+183, @CGUID+180, 0, 0, 3),
|
||||
-- Kael Trash 2
|
||||
(@CGUID+184, @CGUID+184, 3),
|
||||
(@CGUID+185, @CGUID+184, 3),
|
||||
(@CGUID+186, @CGUID+184, 3),
|
||||
(@CGUID+187, @CGUID+184, 3),
|
||||
(@CGUID+184, @CGUID+184, 0, 0, 3),
|
||||
(@CGUID+185, @CGUID+184, 0, 0, 3),
|
||||
(@CGUID+186, @CGUID+184, 0, 0, 3),
|
||||
(@CGUID+187, @CGUID+184, 0, 0, 3),
|
||||
-- Kael Trash 3
|
||||
(@CGUID+188, @CGUID+188, 3),
|
||||
(@CGUID+189, @CGUID+188, 3),
|
||||
(@CGUID+190, @CGUID+188, 3),
|
||||
(@CGUID+191, @CGUID+188, 3),
|
||||
(@CGUID+188, @CGUID+188, 0, 0, 3),
|
||||
(@CGUID+189, @CGUID+188, 0, 0, 3),
|
||||
(@CGUID+190, @CGUID+188, 0, 0, 3),
|
||||
(@CGUID+191, @CGUID+188, 0, 0, 3),
|
||||
-- Kael & Advisors
|
||||
(@CGUID+218, @CGUID+218, 24),
|
||||
(@CGUID+219, @CGUID+218, 24),
|
||||
(@CGUID+220, @CGUID+218, 24),
|
||||
(@CGUID+221, @CGUID+218, 24),
|
||||
(@CGUID+222, @CGUID+218, 24);
|
||||
(@CGUID+218, @CGUID+218, 0, 0, 24),
|
||||
(@CGUID+219, @CGUID+218, 0, 0, 24),
|
||||
(@CGUID+220, @CGUID+218, 0, 0, 24),
|
||||
(@CGUID+221, @CGUID+218, 0, 0, 24),
|
||||
(@CGUID+222, @CGUID+218, 0, 0, 24);
|
||||
|
||||
-- Update SheatheState en masse
|
||||
UPDATE `creature_template_addon` SET `bytes2` = 1 WHERE `entry` IN (18805,19514,19516,19622,20031,20032,20033,20034,20035,20036,20037,20038,20039,20040,20041,20042,20043,20044,20045,20046,20047,20048,20049,20050,20052,20060,20062,20063,20064,22515,22517);
|
||||
|
||||
@@ -3770,411 +3770,411 @@ INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`
|
||||
-- 0x203CA44680168B80007C2000008127DD .go xyz 409.20535 785.9281 14.643406
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (@CGUID+1,@CGUID+2,@CGUID+3,@CGUID+4,@CGUID+5,@CGUID+6,@CGUID+7,@CGUID+8,@CGUID+9,@CGUID+10,@CGUID+11,@CGUID+12,@CGUID+23,@CGUID+24,@CGUID+25,@CGUID+26,@CGUID+27,@CGUID+28,@CGUID+29,@CGUID+30,@CGUID+31,@CGUID+32,@CGUID+33,@CGUID+34,@CGUID+35,@CGUID+36,@CGUID+37,@CGUID+38,@CGUID+39,@CGUID+40,@CGUID+41,@CGUID+42,@CGUID+43,@CGUID+44,@CGUID+45,@CGUID+46,@CGUID+47,@CGUID+48,@CGUID+49,@CGUID+50,@CGUID+51,@CGUID+52,@CGUID+53,@CGUID+54,@CGUID+62,@CGUID+63,@CGUID+64,@CGUID+65,@CGUID+66,@CGUID+67,@CGUID+68,@CGUID+69,@CGUID+70,@CGUID+71,@CGUID+74,@CGUID+75,@CGUID+76,@CGUID+77,@CGUID+78,@CGUID+79,@CGUID+82,@CGUID+83,@CGUID+84,@CGUID+85,@CGUID+86,@CGUID+87,@CGUID+88,@CGUID+89,@CGUID+90,@CGUID+91,@CGUID+92,@CGUID+93,@CGUID+94,@CGUID+95,@CGUID+96,@CGUID+97,@CGUID+98,@CGUID+99,@CGUID+146,@CGUID+147,@CGUID+148,@CGUID+149,@CGUID+150,@CGUID+151,@CGUID+152,@CGUID+153,@CGUID+154,@CGUID+155,@CGUID+156,@CGUID+157,@CGUID+158,@CGUID+159,@CGUID+160,@CGUID+161,@CGUID+162,@CGUID+163,@CGUID+164,@CGUID+165,@CGUID+166,@CGUID+167,@CGUID+168,@CGUID+169,@CGUID+170,@CGUID+171,@CGUID+172,@CGUID+173,@CGUID+174,@CGUID+175,@CGUID+188,@CGUID+189,@CGUID+190,@CGUID+191,@CGUID+192,@CGUID+193,@CGUID+194,@CGUID+195,@CGUID+196,@CGUID+197,@CGUID+198,@CGUID+199,@CGUID+200,@CGUID+201,@CGUID+202,@CGUID+203,@CGUID+204,@CGUID+205,@CGUID+206,@CGUID+207,@CGUID+208,@CGUID+209,@CGUID+210,@CGUID+211,@CGUID+212,@CGUID+213,@CGUID+222,@CGUID+223,@CGUID+224,@CGUID+225,@CGUID+226,@CGUID+227,@CGUID+228,@CGUID+229,@CGUID+253,@CGUID+254,@CGUID+255,@CGUID+256,@CGUID+266,@CGUID+267,@CGUID+268,@CGUID+269,@CGUID+270,@CGUID+271,@CGUID+272,@CGUID+273,@CGUID+274,@CGUID+275,@CGUID+276,@CGUID+277,@CGUID+278,@CGUID+279,@CGUID+280,@CGUID+281,@CGUID+282,@CGUID+283,@CGUID+284,@CGUID+287,@CGUID+289,@CGUID+290,@CGUID+291,@CGUID+292,@CGUID+293,@CGUID+294,@CGUID+295,@CGUID+296,@CGUID+297,@CGUID+298,@CGUID+299,@CGUID+300,@CGUID+301,@CGUID+302,@CGUID+303,@CGUID+304,@CGUID+305,@CGUID+318,@CGUID+319,@CGUID+320,@CGUID+321,@CGUID+322,@CGUID+323,@CGUID+324,@CGUID+325,@CGUID+326,@CGUID+327,@CGUID+328,@CGUID+329,@CGUID+330,@CGUID+331,@CGUID+365,@CGUID+366,@CGUID+367,@CGUID+368,@CGUID+369,@CGUID+370,@CGUID+371,@CGUID+372,@CGUID+373,@CGUID+374,@CGUID+375,@CGUID+376,@CGUID+377,@CGUID+378,@CGUID+379,@CGUID+380,@CGUID+383,@CGUID+384,@CGUID+385,@CGUID+386,@CGUID+387,@CGUID+388,@CGUID+389,@CGUID+390,@CGUID+391,@CGUID+392,@CGUID+393,@CGUID+396,@CGUID+397,@CGUID+398,@CGUID+399,@CGUID+400,@CGUID+401,@CGUID+402,@CGUID+403,@CGUID+404,@CGUID+405,@CGUID+406,@CGUID+418,@CGUID+419,@CGUID+420,@CGUID+421,@CGUID+422,@CGUID+423,@CGUID+540,@CGUID+541,@CGUID+542,@CGUID+543,@CGUID+544,@CGUID+545,@CGUID+546,@CGUID+547,@CGUID+548,@CGUID+549,@CGUID+550,@CGUID+551,@CGUID+552,@CGUID+553,@CGUID+554,@CGUID+555,@CGUID+556,@CGUID+557,@CGUID+558,@CGUID+559,@CGUID+560,@CGUID+561,@CGUID+562,@CGUID+563,@CGUID+564,@CGUID+565,@CGUID+566,@CGUID+567,@CGUID+568,@CGUID+569,@CGUID+570,@CGUID+571,@CGUID+572,@CGUID+573,@CGUID+574,@CGUID+575,@CGUID+576,@CGUID+577,@CGUID+578,@CGUID+579,@CGUID+580,@CGUID+581,@CGUID+582,@CGUID+583,@CGUID+584,@CGUID+585,@CGUID+586,@CGUID+587,@CGUID+588,@CGUID+589,@CGUID+592,@CGUID+593,@CGUID+594,@CGUID+595,@CGUID+596,@CGUID+597,@CGUID+598,@CGUID+599,@CGUID+600,@CGUID+601,@CGUID+602,@CGUID+603,@CGUID+605,@CGUID+606,@CGUID+607,@CGUID+608,@CGUID+609,@CGUID+610,@CGUID+611,@CGUID+612,@CGUID+613,@CGUID+614,@CGUID+615,@CGUID+616,@CGUID+617,@CGUID+618,@CGUID+619,@CGUID+620,@CGUID+621,@CGUID+622,@CGUID+623,@CGUID+624,@CGUID+625,@CGUID+626,@CGUID+627,@CGUID+628,@CGUID+629,@CGUID+630,@CGUID+631,@CGUID+632,@CGUID+633,@CGUID+634,@CGUID+635,@CGUID+636,@CGUID+637,@CGUID+638,@CGUID+639,@CGUID+640,@CGUID+641,@CGUID+642,@CGUID+643,@CGUID+644,@CGUID+645,@CGUID+646,@CGUID+647,@CGUID+648,@CGUID+649,@CGUID+650,@CGUID+651,@CGUID+652,@CGUID+653,@CGUID+654,@CGUID+655,@CGUID+656,@CGUID+657,@CGUID+658,@CGUID+659,@CGUID+660,@CGUID+661,@CGUID+662,@CGUID+663,@CGUID+664,@CGUID+665,@CGUID+666,@CGUID+667,@CGUID+668,@CGUID+669,@CGUID+670,@CGUID+671,@CGUID+672,@CGUID+673,@CGUID+674,@CGUID+675,@CGUID+676,@CGUID+677,@CGUID+691,@CGUID+692,@CGUID+693,@CGUID+694,@CGUID+695,@CGUID+696,@CGUID+697,@CGUID+698,@CGUID+699,@CGUID+700,@CGUID+701,@CGUID+702,@CGUID+703,@CGUID+704,@CGUID+705,@CGUID+706,@CGUID+707,@CGUID+708,@CGUID+709,@CGUID+710,@CGUID+711,@CGUID+712,@CGUID+713,@CGUID+714,@CGUID+716,@CGUID+717,@CGUID+718,@CGUID+719) AND `groupAI` IN (3, 27);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `groupAI`) VALUES
|
||||
(@CGUID+1 , @CGUID+1 , 3),
|
||||
(@CGUID+2 , @CGUID+1 , 3),
|
||||
(@CGUID+3 , @CGUID+1 , 3),
|
||||
(@CGUID+4 , @CGUID+4 , 3),
|
||||
(@CGUID+5 , @CGUID+4 , 3),
|
||||
(@CGUID+6 , @CGUID+4 , 3),
|
||||
(@CGUID+7 , @CGUID+7 , 3),
|
||||
(@CGUID+8 , @CGUID+7 , 3),
|
||||
(@CGUID+9 , @CGUID+7 , 3),
|
||||
(@CGUID+10 , @CGUID+10 , 3),
|
||||
(@CGUID+11 , @CGUID+10 , 3),
|
||||
(@CGUID+12 , @CGUID+10 , 3),
|
||||
(@CGUID+23 , @CGUID+23 , 3),
|
||||
(@CGUID+24 , @CGUID+23 , 3),
|
||||
(@CGUID+25 , @CGUID+23 , 3),
|
||||
(@CGUID+26 , @CGUID+23 , 3),
|
||||
(@CGUID+27 , @CGUID+23 , 3),
|
||||
(@CGUID+28 , @CGUID+23 , 3),
|
||||
(@CGUID+29 , @CGUID+23 , 3),
|
||||
(@CGUID+30 , @CGUID+23 , 3),
|
||||
(@CGUID+31 , @CGUID+23 , 3),
|
||||
(@CGUID+32 , @CGUID+23 , 3),
|
||||
(@CGUID+33 , @CGUID+33 , 3),
|
||||
(@CGUID+34 , @CGUID+33 , 3),
|
||||
(@CGUID+35 , @CGUID+33 , 3),
|
||||
(@CGUID+36 , @CGUID+33 , 3),
|
||||
(@CGUID+37 , @CGUID+33 , 3),
|
||||
(@CGUID+38 , @CGUID+33 , 3),
|
||||
(@CGUID+39 , @CGUID+33 , 3),
|
||||
(@CGUID+40 , @CGUID+33 , 3),
|
||||
(@CGUID+41 , @CGUID+33 , 3),
|
||||
(@CGUID+42 , @CGUID+33 , 3),
|
||||
(@CGUID+43 , @CGUID+43 , 3),
|
||||
(@CGUID+44 , @CGUID+43 , 3),
|
||||
(@CGUID+45 , @CGUID+43 , 3),
|
||||
(@CGUID+46 , @CGUID+43 , 3),
|
||||
(@CGUID+47 , @CGUID+43 , 3),
|
||||
(@CGUID+48 , @CGUID+43 , 3),
|
||||
(@CGUID+49 , @CGUID+49 , 3),
|
||||
(@CGUID+50 , @CGUID+49 , 3),
|
||||
(@CGUID+51 , @CGUID+49 , 3),
|
||||
(@CGUID+52 , @CGUID+49 , 3),
|
||||
(@CGUID+53 , @CGUID+49 , 3),
|
||||
(@CGUID+54 , @CGUID+49 , 3),
|
||||
(@CGUID+62 , @CGUID+62 , 27),
|
||||
(@CGUID+63 , @CGUID+62 , 27),
|
||||
(@CGUID+64 , @CGUID+62 , 27),
|
||||
(@CGUID+65 , @CGUID+65 , 3),
|
||||
(@CGUID+66 , @CGUID+65 , 3),
|
||||
(@CGUID+67 , @CGUID+65 , 3),
|
||||
(@CGUID+68 , @CGUID+65 , 3),
|
||||
(@CGUID+69 , @CGUID+65 , 3),
|
||||
(@CGUID+70 , @CGUID+65 , 3),
|
||||
(@CGUID+71 , @CGUID+65 , 3),
|
||||
(@CGUID+74 , @CGUID+74 , 3),
|
||||
(@CGUID+75 , @CGUID+74 , 3),
|
||||
(@CGUID+76 , @CGUID+74 , 3),
|
||||
(@CGUID+77 , @CGUID+74 , 3),
|
||||
(@CGUID+78 , @CGUID+74 , 3),
|
||||
(@CGUID+79 , @CGUID+74 , 3),
|
||||
(@CGUID+82 , @CGUID+82 , 3),
|
||||
(@CGUID+83 , @CGUID+82 , 3),
|
||||
(@CGUID+84 , @CGUID+82 , 3),
|
||||
(@CGUID+85 , @CGUID+82 , 3),
|
||||
(@CGUID+86 , @CGUID+82 , 3),
|
||||
(@CGUID+87 , @CGUID+87 , 3),
|
||||
(@CGUID+88 , @CGUID+87 , 3),
|
||||
(@CGUID+89 , @CGUID+87 , 3),
|
||||
(@CGUID+90 , @CGUID+87 , 3),
|
||||
(@CGUID+91 , @CGUID+87 , 3),
|
||||
(@CGUID+92 , @CGUID+87 , 3),
|
||||
(@CGUID+93 , @CGUID+87 , 3),
|
||||
(@CGUID+94 , @CGUID+94 , 3),
|
||||
(@CGUID+95 , @CGUID+94 , 3),
|
||||
(@CGUID+96 , @CGUID+94 , 3),
|
||||
(@CGUID+97 , @CGUID+94 , 3),
|
||||
(@CGUID+98 , @CGUID+94 , 3),
|
||||
(@CGUID+99 , @CGUID+94 , 3),
|
||||
(@CGUID+146, @CGUID+146, 3),
|
||||
(@CGUID+147, @CGUID+146, 3),
|
||||
(@CGUID+148, @CGUID+146, 3),
|
||||
(@CGUID+149, @CGUID+146, 3),
|
||||
(@CGUID+150, @CGUID+146, 3),
|
||||
(@CGUID+151, @CGUID+146, 3),
|
||||
(@CGUID+152, @CGUID+152, 3),
|
||||
(@CGUID+153, @CGUID+152, 3),
|
||||
(@CGUID+154, @CGUID+152, 3),
|
||||
(@CGUID+155, @CGUID+152, 3),
|
||||
(@CGUID+156, @CGUID+152, 3),
|
||||
(@CGUID+157, @CGUID+152, 3),
|
||||
(@CGUID+158, @CGUID+158, 3),
|
||||
(@CGUID+159, @CGUID+158, 3),
|
||||
(@CGUID+160, @CGUID+158, 3),
|
||||
(@CGUID+161, @CGUID+158, 3),
|
||||
(@CGUID+162, @CGUID+158, 3),
|
||||
(@CGUID+163, @CGUID+158, 3),
|
||||
(@CGUID+164, @CGUID+164, 3),
|
||||
(@CGUID+165, @CGUID+164, 3),
|
||||
(@CGUID+166, @CGUID+164, 3),
|
||||
(@CGUID+167, @CGUID+164, 3),
|
||||
(@CGUID+168, @CGUID+164, 3),
|
||||
(@CGUID+169, @CGUID+164, 3),
|
||||
(@CGUID+170, @CGUID+170, 3),
|
||||
(@CGUID+171, @CGUID+170, 3),
|
||||
(@CGUID+172, @CGUID+170, 3),
|
||||
(@CGUID+173, @CGUID+170, 3),
|
||||
(@CGUID+174, @CGUID+170, 3),
|
||||
(@CGUID+175, @CGUID+170, 3),
|
||||
(@CGUID+188, @CGUID+188, 3),
|
||||
(@CGUID+189, @CGUID+188, 3),
|
||||
(@CGUID+190, @CGUID+190, 3),
|
||||
(@CGUID+191, @CGUID+190, 3),
|
||||
(@CGUID+192, @CGUID+192, 3),
|
||||
(@CGUID+193, @CGUID+192, 3),
|
||||
(@CGUID+194, @CGUID+194, 3),
|
||||
(@CGUID+195, @CGUID+194, 3),
|
||||
(@CGUID+196, @CGUID+196, 3),
|
||||
(@CGUID+197, @CGUID+196, 3),
|
||||
(@CGUID+198, @CGUID+198, 3),
|
||||
(@CGUID+199, @CGUID+198, 3),
|
||||
(@CGUID+200, @CGUID+198, 3),
|
||||
(@CGUID+201, @CGUID+198, 3),
|
||||
(@CGUID+202, @CGUID+198, 3),
|
||||
(@CGUID+203, @CGUID+198, 3),
|
||||
(@CGUID+204, @CGUID+198, 3),
|
||||
(@CGUID+205, @CGUID+198, 3),
|
||||
(@CGUID+206, @CGUID+206, 3),
|
||||
(@CGUID+207, @CGUID+206, 3),
|
||||
(@CGUID+208, @CGUID+206, 3),
|
||||
(@CGUID+209, @CGUID+206, 3),
|
||||
(@CGUID+210, @CGUID+206, 3),
|
||||
(@CGUID+211, @CGUID+206, 3),
|
||||
(@CGUID+212, @CGUID+206, 3),
|
||||
(@CGUID+213, @CGUID+206, 3),
|
||||
(@CGUID+222, @CGUID+222, 3),
|
||||
(@CGUID+223, @CGUID+222, 3),
|
||||
(@CGUID+224, @CGUID+222, 3),
|
||||
(@CGUID+225, @CGUID+222, 3),
|
||||
(@CGUID+226, @CGUID+226, 3),
|
||||
(@CGUID+227, @CGUID+226, 3),
|
||||
(@CGUID+228, @CGUID+226, 3),
|
||||
(@CGUID+229, @CGUID+226, 3),
|
||||
(@CGUID+253, @CGUID+253, 3),
|
||||
(@CGUID+254, @CGUID+253, 3),
|
||||
(@CGUID+255, @CGUID+253, 3),
|
||||
(@CGUID+256, @CGUID+253, 3),
|
||||
(@CGUID+266, @CGUID+266, 3),
|
||||
(@CGUID+267, @CGUID+266, 3),
|
||||
(@CGUID+268, @CGUID+266, 3),
|
||||
(@CGUID+269, @CGUID+266, 3),
|
||||
(@CGUID+270, @CGUID+266, 3),
|
||||
(@CGUID+271, @CGUID+266, 3),
|
||||
(@CGUID+272, @CGUID+266, 3),
|
||||
(@CGUID+273, @CGUID+273, 3),
|
||||
(@CGUID+274, @CGUID+273, 3),
|
||||
(@CGUID+275, @CGUID+273, 3),
|
||||
(@CGUID+276, @CGUID+273, 3),
|
||||
(@CGUID+277, @CGUID+273, 3),
|
||||
(@CGUID+278, @CGUID+273, 3),
|
||||
(@CGUID+279, @CGUID+273, 3),
|
||||
(@CGUID+280, @CGUID+280, 3),
|
||||
(@CGUID+281, @CGUID+280, 3),
|
||||
(@CGUID+282, @CGUID+280, 3),
|
||||
(@CGUID+283, @CGUID+280, 3),
|
||||
(@CGUID+284, @CGUID+280, 3),
|
||||
(@CGUID+287, @CGUID+287, 3),
|
||||
(@CGUID+289, @CGUID+287, 3),
|
||||
(@CGUID+290, @CGUID+287, 3),
|
||||
(@CGUID+291, @CGUID+287, 3),
|
||||
(@CGUID+292, @CGUID+287, 3),
|
||||
(@CGUID+293, @CGUID+287, 3),
|
||||
(@CGUID+294, @CGUID+287, 3),
|
||||
(@CGUID+295, @CGUID+287, 3),
|
||||
(@CGUID+296, @CGUID+287, 3),
|
||||
(@CGUID+297, @CGUID+297, 3),
|
||||
(@CGUID+298, @CGUID+297, 3),
|
||||
(@CGUID+299, @CGUID+297, 3),
|
||||
(@CGUID+300, @CGUID+297, 3),
|
||||
(@CGUID+301, @CGUID+297, 3),
|
||||
(@CGUID+302, @CGUID+302, 3),
|
||||
(@CGUID+303, @CGUID+302, 3),
|
||||
(@CGUID+304, @CGUID+302, 3),
|
||||
(@CGUID+305, @CGUID+302, 3),
|
||||
(@CGUID+318, @CGUID+318, 3),
|
||||
(@CGUID+319, @CGUID+318, 3),
|
||||
(@CGUID+320, @CGUID+318, 3),
|
||||
(@CGUID+321, @CGUID+318, 3),
|
||||
(@CGUID+322, @CGUID+318, 3),
|
||||
(@CGUID+323, @CGUID+323, 3),
|
||||
(@CGUID+324, @CGUID+323, 3),
|
||||
(@CGUID+325, @CGUID+323, 3),
|
||||
(@CGUID+326, @CGUID+323, 3),
|
||||
(@CGUID+327, @CGUID+323, 3),
|
||||
(@CGUID+328, @CGUID+328, 3),
|
||||
(@CGUID+329, @CGUID+328, 3),
|
||||
(@CGUID+330, @CGUID+330, 3),
|
||||
(@CGUID+331, @CGUID+330, 3),
|
||||
(@CGUID+365, @CGUID+365, 3),
|
||||
(@CGUID+366, @CGUID+365, 3),
|
||||
(@CGUID+367, @CGUID+365, 3),
|
||||
(@CGUID+368, @CGUID+365, 3),
|
||||
(@CGUID+369, @CGUID+365, 3),
|
||||
(@CGUID+370, @CGUID+365, 3),
|
||||
(@CGUID+371, @CGUID+365, 3),
|
||||
(@CGUID+372, @CGUID+365, 3),
|
||||
(@CGUID+373, @CGUID+373, 3),
|
||||
(@CGUID+374, @CGUID+373, 3),
|
||||
(@CGUID+375, @CGUID+373, 3),
|
||||
(@CGUID+376, @CGUID+373, 3),
|
||||
(@CGUID+377, @CGUID+373, 3),
|
||||
(@CGUID+378, @CGUID+373, 3),
|
||||
(@CGUID+379, @CGUID+373, 3),
|
||||
(@CGUID+380, @CGUID+373, 3),
|
||||
(@CGUID+383, @CGUID+383, 3),
|
||||
(@CGUID+384, @CGUID+383, 3),
|
||||
(@CGUID+385, @CGUID+383, 3),
|
||||
(@CGUID+386, @CGUID+383, 3),
|
||||
(@CGUID+387, @CGUID+383, 3),
|
||||
(@CGUID+388, @CGUID+383, 3),
|
||||
(@CGUID+389, @CGUID+383, 3),
|
||||
(@CGUID+390, @CGUID+383, 3),
|
||||
(@CGUID+391, @CGUID+383, 3),
|
||||
(@CGUID+392, @CGUID+383, 3),
|
||||
(@CGUID+393, @CGUID+383, 3),
|
||||
(@CGUID+396, @CGUID+396, 3),
|
||||
(@CGUID+397, @CGUID+396, 3),
|
||||
(@CGUID+398, @CGUID+396, 3),
|
||||
(@CGUID+399, @CGUID+396, 3),
|
||||
(@CGUID+400, @CGUID+396, 3),
|
||||
(@CGUID+401, @CGUID+396, 3),
|
||||
(@CGUID+402, @CGUID+396, 3),
|
||||
(@CGUID+403, @CGUID+396, 3),
|
||||
(@CGUID+404, @CGUID+396, 3),
|
||||
(@CGUID+405, @CGUID+396, 3),
|
||||
(@CGUID+406, @CGUID+396, 3),
|
||||
(@CGUID+418, @CGUID+418, 3),
|
||||
(@CGUID+419, @CGUID+418, 3),
|
||||
(@CGUID+420, @CGUID+418, 3),
|
||||
(@CGUID+421, @CGUID+418, 3),
|
||||
(@CGUID+422, @CGUID+418, 3),
|
||||
(@CGUID+423, @CGUID+418, 3),
|
||||
(@CGUID+540, @CGUID+540, 3),
|
||||
(@CGUID+541, @CGUID+540, 3),
|
||||
(@CGUID+542, @CGUID+540, 3),
|
||||
(@CGUID+543, @CGUID+540, 3),
|
||||
(@CGUID+544, @CGUID+540, 3),
|
||||
(@CGUID+545, @CGUID+540, 3),
|
||||
(@CGUID+546, @CGUID+540, 3),
|
||||
(@CGUID+547, @CGUID+540, 3),
|
||||
(@CGUID+548, @CGUID+540, 3),
|
||||
(@CGUID+549, @CGUID+540, 3),
|
||||
(@CGUID+550, @CGUID+550, 3),
|
||||
(@CGUID+551, @CGUID+550, 3),
|
||||
(@CGUID+552, @CGUID+550, 3),
|
||||
(@CGUID+553, @CGUID+550, 3),
|
||||
(@CGUID+554, @CGUID+550, 3),
|
||||
(@CGUID+555, @CGUID+550, 3),
|
||||
(@CGUID+556, @CGUID+550, 3),
|
||||
(@CGUID+557, @CGUID+550, 3),
|
||||
(@CGUID+558, @CGUID+550, 3),
|
||||
(@CGUID+559, @CGUID+550, 3),
|
||||
(@CGUID+560, @CGUID+560, 3),
|
||||
(@CGUID+561, @CGUID+560, 3),
|
||||
(@CGUID+562, @CGUID+560, 3),
|
||||
(@CGUID+563, @CGUID+560, 3),
|
||||
(@CGUID+564, @CGUID+560, 3),
|
||||
(@CGUID+565, @CGUID+560, 3),
|
||||
(@CGUID+566, @CGUID+560, 3),
|
||||
(@CGUID+567, @CGUID+560, 3),
|
||||
(@CGUID+568, @CGUID+560, 3),
|
||||
(@CGUID+569, @CGUID+560, 3),
|
||||
(@CGUID+570, @CGUID+570, 3),
|
||||
(@CGUID+571, @CGUID+570, 3),
|
||||
(@CGUID+572, @CGUID+570, 3),
|
||||
(@CGUID+573, @CGUID+570, 3),
|
||||
(@CGUID+574, @CGUID+570, 3),
|
||||
(@CGUID+575, @CGUID+570, 3),
|
||||
(@CGUID+576, @CGUID+570, 3),
|
||||
(@CGUID+577, @CGUID+570, 3),
|
||||
(@CGUID+578, @CGUID+570, 3),
|
||||
(@CGUID+579, @CGUID+570, 3),
|
||||
(@CGUID+580, @CGUID+580, 3),
|
||||
(@CGUID+581, @CGUID+580, 3),
|
||||
(@CGUID+582, @CGUID+580, 3),
|
||||
(@CGUID+583, @CGUID+580, 3),
|
||||
(@CGUID+584, @CGUID+580, 3),
|
||||
(@CGUID+585, @CGUID+580, 3),
|
||||
(@CGUID+586, @CGUID+580, 3),
|
||||
(@CGUID+587, @CGUID+580, 3),
|
||||
(@CGUID+588, @CGUID+580, 3),
|
||||
(@CGUID+589, @CGUID+580, 3),
|
||||
(@CGUID+592, @CGUID+592, 3),
|
||||
(@CGUID+593, @CGUID+592, 3),
|
||||
(@CGUID+594, @CGUID+592, 3),
|
||||
(@CGUID+595, @CGUID+592, 3),
|
||||
(@CGUID+596, @CGUID+592, 3),
|
||||
(@CGUID+597, @CGUID+592, 3),
|
||||
(@CGUID+598, @CGUID+592, 3),
|
||||
(@CGUID+599, @CGUID+592, 3),
|
||||
(@CGUID+600, @CGUID+592, 3),
|
||||
(@CGUID+601, @CGUID+592, 3),
|
||||
(@CGUID+602, @CGUID+602, 3),
|
||||
(@CGUID+603, @CGUID+602, 3),
|
||||
(@CGUID+605, @CGUID+605, 3),
|
||||
(@CGUID+606, @CGUID+605, 3),
|
||||
(@CGUID+607, @CGUID+605, 3),
|
||||
(@CGUID+608, @CGUID+605, 3),
|
||||
(@CGUID+609, @CGUID+605, 3),
|
||||
(@CGUID+610, @CGUID+605, 3),
|
||||
(@CGUID+611, @CGUID+605, 3),
|
||||
(@CGUID+612, @CGUID+605, 3),
|
||||
(@CGUID+613, @CGUID+605, 3),
|
||||
(@CGUID+614, @CGUID+605, 3),
|
||||
(@CGUID+615, @CGUID+615, 3),
|
||||
(@CGUID+616, @CGUID+615, 3),
|
||||
(@CGUID+617, @CGUID+615, 3),
|
||||
(@CGUID+618, @CGUID+615, 3),
|
||||
(@CGUID+619, @CGUID+615, 3),
|
||||
(@CGUID+620, @CGUID+615, 3),
|
||||
(@CGUID+621, @CGUID+615, 3),
|
||||
(@CGUID+622, @CGUID+615, 3),
|
||||
(@CGUID+623, @CGUID+615, 3),
|
||||
(@CGUID+624, @CGUID+615, 3),
|
||||
(@CGUID+625, @CGUID+625, 3),
|
||||
(@CGUID+626, @CGUID+625, 3),
|
||||
(@CGUID+627, @CGUID+625, 3),
|
||||
(@CGUID+628, @CGUID+628, 3),
|
||||
(@CGUID+629, @CGUID+628, 3),
|
||||
(@CGUID+630, @CGUID+628, 3),
|
||||
(@CGUID+631, @CGUID+631, 3),
|
||||
(@CGUID+632, @CGUID+631, 3),
|
||||
(@CGUID+633, @CGUID+631, 3),
|
||||
(@CGUID+634, @CGUID+631, 3),
|
||||
(@CGUID+635, @CGUID+635, 3),
|
||||
(@CGUID+636, @CGUID+635, 3),
|
||||
(@CGUID+637, @CGUID+635, 3),
|
||||
(@CGUID+638, @CGUID+638, 3),
|
||||
(@CGUID+639, @CGUID+638, 3),
|
||||
(@CGUID+640, @CGUID+638, 3),
|
||||
(@CGUID+641, @CGUID+638, 3),
|
||||
(@CGUID+642, @CGUID+638, 3),
|
||||
(@CGUID+643, @CGUID+638, 3),
|
||||
(@CGUID+644, @CGUID+644, 3),
|
||||
(@CGUID+645, @CGUID+644, 3),
|
||||
(@CGUID+646, @CGUID+644, 3),
|
||||
(@CGUID+647, @CGUID+644, 3),
|
||||
(@CGUID+648, @CGUID+648, 3),
|
||||
(@CGUID+649, @CGUID+648, 3),
|
||||
(@CGUID+650, @CGUID+648, 3),
|
||||
(@CGUID+651, @CGUID+648, 3),
|
||||
(@CGUID+652, @CGUID+648, 3),
|
||||
(@CGUID+653, @CGUID+648, 3),
|
||||
(@CGUID+654, @CGUID+654, 3),
|
||||
(@CGUID+655, @CGUID+654, 3),
|
||||
(@CGUID+656, @CGUID+654, 3),
|
||||
(@CGUID+657, @CGUID+654, 3),
|
||||
(@CGUID+658, @CGUID+658, 3),
|
||||
(@CGUID+659, @CGUID+658, 3),
|
||||
(@CGUID+660, @CGUID+658, 3),
|
||||
(@CGUID+661, @CGUID+658, 3),
|
||||
(@CGUID+662, @CGUID+658, 3),
|
||||
(@CGUID+663, @CGUID+658, 3),
|
||||
(@CGUID+664, @CGUID+658, 3),
|
||||
(@CGUID+665, @CGUID+658, 3),
|
||||
(@CGUID+666, @CGUID+658, 3),
|
||||
(@CGUID+667, @CGUID+658, 3),
|
||||
(@CGUID+668, @CGUID+668, 3),
|
||||
(@CGUID+669, @CGUID+668, 3),
|
||||
(@CGUID+670, @CGUID+668, 3),
|
||||
(@CGUID+671, @CGUID+668, 3),
|
||||
(@CGUID+672, @CGUID+668, 3),
|
||||
(@CGUID+673, @CGUID+673, 3),
|
||||
(@CGUID+674, @CGUID+673, 3),
|
||||
(@CGUID+675, @CGUID+673, 3),
|
||||
(@CGUID+676, @CGUID+673, 3),
|
||||
(@CGUID+677, @CGUID+673, 3),
|
||||
(@CGUID+691, @CGUID+691, 3),
|
||||
(@CGUID+692, @CGUID+691, 3),
|
||||
(@CGUID+693, @CGUID+691, 3),
|
||||
(@CGUID+694, @CGUID+691, 3),
|
||||
(@CGUID+695, @CGUID+691, 3),
|
||||
(@CGUID+696, @CGUID+691, 3),
|
||||
(@CGUID+697, @CGUID+697, 3),
|
||||
(@CGUID+698, @CGUID+697, 3),
|
||||
(@CGUID+699, @CGUID+697, 3),
|
||||
(@CGUID+700, @CGUID+697, 3),
|
||||
(@CGUID+701, @CGUID+697, 3),
|
||||
(@CGUID+702, @CGUID+697, 3),
|
||||
(@CGUID+703, @CGUID+703, 3),
|
||||
(@CGUID+704, @CGUID+703, 3),
|
||||
(@CGUID+705, @CGUID+703, 3),
|
||||
(@CGUID+706, @CGUID+703, 3),
|
||||
(@CGUID+707, @CGUID+703, 3),
|
||||
(@CGUID+708, @CGUID+703, 3),
|
||||
(@CGUID+709, @CGUID+709, 3),
|
||||
(@CGUID+710, @CGUID+709, 3),
|
||||
(@CGUID+711, @CGUID+709, 3),
|
||||
(@CGUID+712, @CGUID+709, 3),
|
||||
(@CGUID+713, @CGUID+709, 3),
|
||||
(@CGUID+714, @CGUID+709, 3),
|
||||
(@CGUID+716, @CGUID+716, 3),
|
||||
(@CGUID+717, @CGUID+716, 3),
|
||||
(@CGUID+718, @CGUID+716, 3),
|
||||
(@CGUID+719, @CGUID+716, 3);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(@CGUID+1, @CGUID+1, 0, 0, 3),
|
||||
(@CGUID+2, @CGUID+1, 0, 0, 3),
|
||||
(@CGUID+3, @CGUID+1, 0, 0, 3),
|
||||
(@CGUID+4, @CGUID+4, 0, 0, 3),
|
||||
(@CGUID+5, @CGUID+4, 0, 0, 3),
|
||||
(@CGUID+6, @CGUID+4, 0, 0, 3),
|
||||
(@CGUID+7, @CGUID+7, 0, 0, 3),
|
||||
(@CGUID+8, @CGUID+7, 0, 0, 3),
|
||||
(@CGUID+9, @CGUID+7, 0, 0, 3),
|
||||
(@CGUID+10, @CGUID+10, 0, 0, 3),
|
||||
(@CGUID+11, @CGUID+10, 0, 0, 3),
|
||||
(@CGUID+12, @CGUID+10, 0, 0, 3),
|
||||
(@CGUID+23, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+24, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+25, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+27, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+28, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+29, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+30, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+31, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+32, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+33, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+34, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+35, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+36, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+37, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+38, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+39, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+40, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+41, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+42, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+43, @CGUID+43, 0, 0, 3),
|
||||
(@CGUID+44, @CGUID+43, 0, 0, 3),
|
||||
(@CGUID+45, @CGUID+43, 0, 0, 3),
|
||||
(@CGUID+46, @CGUID+43, 0, 0, 3),
|
||||
(@CGUID+47, @CGUID+43, 0, 0, 3),
|
||||
(@CGUID+48, @CGUID+43, 0, 0, 3),
|
||||
(@CGUID+49, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+50, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+51, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+52, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+53, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+54, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+62, @CGUID+62, 0, 0, 27),
|
||||
(@CGUID+63, @CGUID+62, 0, 0, 27),
|
||||
(@CGUID+64, @CGUID+62, 0, 0, 27),
|
||||
(@CGUID+65, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+66, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+67, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+68, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+69, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+70, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+71, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+74, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+75, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+76, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+77, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+78, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+79, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+82, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+83, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+84, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+85, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+86, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+87, @CGUID+87, 0, 0, 3),
|
||||
(@CGUID+88, @CGUID+87, 0, 0, 3),
|
||||
(@CGUID+89, @CGUID+87, 0, 0, 3),
|
||||
(@CGUID+90, @CGUID+87, 0, 0, 3),
|
||||
(@CGUID+91, @CGUID+87, 0, 0, 3),
|
||||
(@CGUID+92, @CGUID+87, 0, 0, 3),
|
||||
(@CGUID+93, @CGUID+87, 0, 0, 3),
|
||||
(@CGUID+94, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+95, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+96, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+97, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+98, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+99, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+146, @CGUID+146, 0, 0, 3),
|
||||
(@CGUID+147, @CGUID+146, 0, 0, 3),
|
||||
(@CGUID+148, @CGUID+146, 0, 0, 3),
|
||||
(@CGUID+149, @CGUID+146, 0, 0, 3),
|
||||
(@CGUID+150, @CGUID+146, 0, 0, 3),
|
||||
(@CGUID+151, @CGUID+146, 0, 0, 3),
|
||||
(@CGUID+152, @CGUID+152, 0, 0, 3),
|
||||
(@CGUID+153, @CGUID+152, 0, 0, 3),
|
||||
(@CGUID+154, @CGUID+152, 0, 0, 3),
|
||||
(@CGUID+155, @CGUID+152, 0, 0, 3),
|
||||
(@CGUID+156, @CGUID+152, 0, 0, 3),
|
||||
(@CGUID+157, @CGUID+152, 0, 0, 3),
|
||||
(@CGUID+158, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+160, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+161, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+162, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+163, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+164, 0, 0, 3),
|
||||
(@CGUID+165, @CGUID+164, 0, 0, 3),
|
||||
(@CGUID+166, @CGUID+164, 0, 0, 3),
|
||||
(@CGUID+167, @CGUID+164, 0, 0, 3),
|
||||
(@CGUID+168, @CGUID+164, 0, 0, 3),
|
||||
(@CGUID+169, @CGUID+164, 0, 0, 3),
|
||||
(@CGUID+170, @CGUID+170, 0, 0, 3),
|
||||
(@CGUID+171, @CGUID+170, 0, 0, 3),
|
||||
(@CGUID+172, @CGUID+170, 0, 0, 3),
|
||||
(@CGUID+173, @CGUID+170, 0, 0, 3),
|
||||
(@CGUID+174, @CGUID+170, 0, 0, 3),
|
||||
(@CGUID+175, @CGUID+170, 0, 0, 3),
|
||||
(@CGUID+188, @CGUID+188, 0, 0, 3),
|
||||
(@CGUID+189, @CGUID+188, 0, 0, 3),
|
||||
(@CGUID+190, @CGUID+190, 0, 0, 3),
|
||||
(@CGUID+191, @CGUID+190, 0, 0, 3),
|
||||
(@CGUID+192, @CGUID+192, 0, 0, 3),
|
||||
(@CGUID+193, @CGUID+192, 0, 0, 3),
|
||||
(@CGUID+194, @CGUID+194, 0, 0, 3),
|
||||
(@CGUID+195, @CGUID+194, 0, 0, 3),
|
||||
(@CGUID+196, @CGUID+196, 0, 0, 3),
|
||||
(@CGUID+197, @CGUID+196, 0, 0, 3),
|
||||
(@CGUID+198, @CGUID+198, 0, 0, 3),
|
||||
(@CGUID+199, @CGUID+198, 0, 0, 3),
|
||||
(@CGUID+200, @CGUID+198, 0, 0, 3),
|
||||
(@CGUID+201, @CGUID+198, 0, 0, 3),
|
||||
(@CGUID+202, @CGUID+198, 0, 0, 3),
|
||||
(@CGUID+203, @CGUID+198, 0, 0, 3),
|
||||
(@CGUID+204, @CGUID+198, 0, 0, 3),
|
||||
(@CGUID+205, @CGUID+198, 0, 0, 3),
|
||||
(@CGUID+206, @CGUID+206, 0, 0, 3),
|
||||
(@CGUID+207, @CGUID+206, 0, 0, 3),
|
||||
(@CGUID+208, @CGUID+206, 0, 0, 3),
|
||||
(@CGUID+209, @CGUID+206, 0, 0, 3),
|
||||
(@CGUID+210, @CGUID+206, 0, 0, 3),
|
||||
(@CGUID+211, @CGUID+206, 0, 0, 3),
|
||||
(@CGUID+212, @CGUID+206, 0, 0, 3),
|
||||
(@CGUID+213, @CGUID+206, 0, 0, 3),
|
||||
(@CGUID+222, @CGUID+222, 0, 0, 3),
|
||||
(@CGUID+223, @CGUID+222, 0, 0, 3),
|
||||
(@CGUID+224, @CGUID+222, 0, 0, 3),
|
||||
(@CGUID+225, @CGUID+222, 0, 0, 3),
|
||||
(@CGUID+226, @CGUID+226, 0, 0, 3),
|
||||
(@CGUID+227, @CGUID+226, 0, 0, 3),
|
||||
(@CGUID+228, @CGUID+226, 0, 0, 3),
|
||||
(@CGUID+229, @CGUID+226, 0, 0, 3),
|
||||
(@CGUID+253, @CGUID+253, 0, 0, 3),
|
||||
(@CGUID+254, @CGUID+253, 0, 0, 3),
|
||||
(@CGUID+255, @CGUID+253, 0, 0, 3),
|
||||
(@CGUID+256, @CGUID+253, 0, 0, 3),
|
||||
(@CGUID+266, @CGUID+266, 0, 0, 3),
|
||||
(@CGUID+267, @CGUID+266, 0, 0, 3),
|
||||
(@CGUID+268, @CGUID+266, 0, 0, 3),
|
||||
(@CGUID+269, @CGUID+266, 0, 0, 3),
|
||||
(@CGUID+270, @CGUID+266, 0, 0, 3),
|
||||
(@CGUID+271, @CGUID+266, 0, 0, 3),
|
||||
(@CGUID+272, @CGUID+266, 0, 0, 3),
|
||||
(@CGUID+273, @CGUID+273, 0, 0, 3),
|
||||
(@CGUID+274, @CGUID+273, 0, 0, 3),
|
||||
(@CGUID+275, @CGUID+273, 0, 0, 3),
|
||||
(@CGUID+276, @CGUID+273, 0, 0, 3),
|
||||
(@CGUID+277, @CGUID+273, 0, 0, 3),
|
||||
(@CGUID+278, @CGUID+273, 0, 0, 3),
|
||||
(@CGUID+279, @CGUID+273, 0, 0, 3),
|
||||
(@CGUID+280, @CGUID+280, 0, 0, 3),
|
||||
(@CGUID+281, @CGUID+280, 0, 0, 3),
|
||||
(@CGUID+282, @CGUID+280, 0, 0, 3),
|
||||
(@CGUID+283, @CGUID+280, 0, 0, 3),
|
||||
(@CGUID+284, @CGUID+280, 0, 0, 3),
|
||||
(@CGUID+287, @CGUID+287, 0, 0, 3),
|
||||
(@CGUID+289, @CGUID+287, 0, 0, 3),
|
||||
(@CGUID+290, @CGUID+287, 0, 0, 3),
|
||||
(@CGUID+291, @CGUID+287, 0, 0, 3),
|
||||
(@CGUID+292, @CGUID+287, 0, 0, 3),
|
||||
(@CGUID+293, @CGUID+287, 0, 0, 3),
|
||||
(@CGUID+294, @CGUID+287, 0, 0, 3),
|
||||
(@CGUID+295, @CGUID+287, 0, 0, 3),
|
||||
(@CGUID+296, @CGUID+287, 0, 0, 3),
|
||||
(@CGUID+297, @CGUID+297, 0, 0, 3),
|
||||
(@CGUID+298, @CGUID+297, 0, 0, 3),
|
||||
(@CGUID+299, @CGUID+297, 0, 0, 3),
|
||||
(@CGUID+300, @CGUID+297, 0, 0, 3),
|
||||
(@CGUID+301, @CGUID+297, 0, 0, 3),
|
||||
(@CGUID+302, @CGUID+302, 0, 0, 3),
|
||||
(@CGUID+303, @CGUID+302, 0, 0, 3),
|
||||
(@CGUID+304, @CGUID+302, 0, 0, 3),
|
||||
(@CGUID+305, @CGUID+302, 0, 0, 3),
|
||||
(@CGUID+318, @CGUID+318, 0, 0, 3),
|
||||
(@CGUID+319, @CGUID+318, 0, 0, 3),
|
||||
(@CGUID+320, @CGUID+318, 0, 0, 3),
|
||||
(@CGUID+321, @CGUID+318, 0, 0, 3),
|
||||
(@CGUID+322, @CGUID+318, 0, 0, 3),
|
||||
(@CGUID+323, @CGUID+323, 0, 0, 3),
|
||||
(@CGUID+324, @CGUID+323, 0, 0, 3),
|
||||
(@CGUID+325, @CGUID+323, 0, 0, 3),
|
||||
(@CGUID+326, @CGUID+323, 0, 0, 3),
|
||||
(@CGUID+327, @CGUID+323, 0, 0, 3),
|
||||
(@CGUID+328, @CGUID+328, 0, 0, 3),
|
||||
(@CGUID+329, @CGUID+328, 0, 0, 3),
|
||||
(@CGUID+330, @CGUID+330, 0, 0, 3),
|
||||
(@CGUID+331, @CGUID+330, 0, 0, 3),
|
||||
(@CGUID+365, @CGUID+365, 0, 0, 3),
|
||||
(@CGUID+366, @CGUID+365, 0, 0, 3),
|
||||
(@CGUID+367, @CGUID+365, 0, 0, 3),
|
||||
(@CGUID+368, @CGUID+365, 0, 0, 3),
|
||||
(@CGUID+369, @CGUID+365, 0, 0, 3),
|
||||
(@CGUID+370, @CGUID+365, 0, 0, 3),
|
||||
(@CGUID+371, @CGUID+365, 0, 0, 3),
|
||||
(@CGUID+372, @CGUID+365, 0, 0, 3),
|
||||
(@CGUID+373, @CGUID+373, 0, 0, 3),
|
||||
(@CGUID+374, @CGUID+373, 0, 0, 3),
|
||||
(@CGUID+375, @CGUID+373, 0, 0, 3),
|
||||
(@CGUID+376, @CGUID+373, 0, 0, 3),
|
||||
(@CGUID+377, @CGUID+373, 0, 0, 3),
|
||||
(@CGUID+378, @CGUID+373, 0, 0, 3),
|
||||
(@CGUID+379, @CGUID+373, 0, 0, 3),
|
||||
(@CGUID+380, @CGUID+373, 0, 0, 3),
|
||||
(@CGUID+383, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+384, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+385, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+386, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+387, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+388, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+389, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+390, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+391, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+392, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+393, @CGUID+383, 0, 0, 3),
|
||||
(@CGUID+396, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+397, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+398, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+399, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+400, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+401, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+402, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+403, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+404, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+405, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+406, @CGUID+396, 0, 0, 3),
|
||||
(@CGUID+418, @CGUID+418, 0, 0, 3),
|
||||
(@CGUID+419, @CGUID+418, 0, 0, 3),
|
||||
(@CGUID+420, @CGUID+418, 0, 0, 3),
|
||||
(@CGUID+421, @CGUID+418, 0, 0, 3),
|
||||
(@CGUID+422, @CGUID+418, 0, 0, 3),
|
||||
(@CGUID+423, @CGUID+418, 0, 0, 3),
|
||||
(@CGUID+540, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+541, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+542, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+543, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+544, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+545, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+546, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+547, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+548, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+549, @CGUID+540, 0, 0, 3),
|
||||
(@CGUID+550, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+551, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+552, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+553, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+554, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+555, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+556, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+557, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+558, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+559, @CGUID+550, 0, 0, 3),
|
||||
(@CGUID+560, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+561, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+562, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+563, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+564, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+565, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+566, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+567, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+568, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+569, @CGUID+560, 0, 0, 3),
|
||||
(@CGUID+570, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+571, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+572, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+573, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+574, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+575, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+576, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+577, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+578, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+579, @CGUID+570, 0, 0, 3),
|
||||
(@CGUID+580, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+581, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+582, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+583, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+584, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+585, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+586, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+587, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+588, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+589, @CGUID+580, 0, 0, 3),
|
||||
(@CGUID+592, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+593, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+594, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+595, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+596, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+597, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+598, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+599, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+600, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+601, @CGUID+592, 0, 0, 3),
|
||||
(@CGUID+602, @CGUID+602, 0, 0, 3),
|
||||
(@CGUID+603, @CGUID+602, 0, 0, 3),
|
||||
(@CGUID+605, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+606, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+607, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+608, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+609, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+610, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+611, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+612, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+613, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+614, @CGUID+605, 0, 0, 3),
|
||||
(@CGUID+615, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+616, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+617, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+618, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+619, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+620, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+621, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+622, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+623, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+624, @CGUID+615, 0, 0, 3),
|
||||
(@CGUID+625, @CGUID+625, 0, 0, 3),
|
||||
(@CGUID+626, @CGUID+625, 0, 0, 3),
|
||||
(@CGUID+627, @CGUID+625, 0, 0, 3),
|
||||
(@CGUID+628, @CGUID+628, 0, 0, 3),
|
||||
(@CGUID+629, @CGUID+628, 0, 0, 3),
|
||||
(@CGUID+630, @CGUID+628, 0, 0, 3),
|
||||
(@CGUID+631, @CGUID+631, 0, 0, 3),
|
||||
(@CGUID+632, @CGUID+631, 0, 0, 3),
|
||||
(@CGUID+633, @CGUID+631, 0, 0, 3),
|
||||
(@CGUID+634, @CGUID+631, 0, 0, 3),
|
||||
(@CGUID+635, @CGUID+635, 0, 0, 3),
|
||||
(@CGUID+636, @CGUID+635, 0, 0, 3),
|
||||
(@CGUID+637, @CGUID+635, 0, 0, 3),
|
||||
(@CGUID+638, @CGUID+638, 0, 0, 3),
|
||||
(@CGUID+639, @CGUID+638, 0, 0, 3),
|
||||
(@CGUID+640, @CGUID+638, 0, 0, 3),
|
||||
(@CGUID+641, @CGUID+638, 0, 0, 3),
|
||||
(@CGUID+642, @CGUID+638, 0, 0, 3),
|
||||
(@CGUID+643, @CGUID+638, 0, 0, 3),
|
||||
(@CGUID+644, @CGUID+644, 0, 0, 3),
|
||||
(@CGUID+645, @CGUID+644, 0, 0, 3),
|
||||
(@CGUID+646, @CGUID+644, 0, 0, 3),
|
||||
(@CGUID+647, @CGUID+644, 0, 0, 3),
|
||||
(@CGUID+648, @CGUID+648, 0, 0, 3),
|
||||
(@CGUID+649, @CGUID+648, 0, 0, 3),
|
||||
(@CGUID+650, @CGUID+648, 0, 0, 3),
|
||||
(@CGUID+651, @CGUID+648, 0, 0, 3),
|
||||
(@CGUID+652, @CGUID+648, 0, 0, 3),
|
||||
(@CGUID+653, @CGUID+648, 0, 0, 3),
|
||||
(@CGUID+654, @CGUID+654, 0, 0, 3),
|
||||
(@CGUID+655, @CGUID+654, 0, 0, 3),
|
||||
(@CGUID+656, @CGUID+654, 0, 0, 3),
|
||||
(@CGUID+657, @CGUID+654, 0, 0, 3),
|
||||
(@CGUID+658, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+659, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+660, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+661, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+662, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+663, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+664, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+665, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+666, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+667, @CGUID+658, 0, 0, 3),
|
||||
(@CGUID+668, @CGUID+668, 0, 0, 3),
|
||||
(@CGUID+669, @CGUID+668, 0, 0, 3),
|
||||
(@CGUID+670, @CGUID+668, 0, 0, 3),
|
||||
(@CGUID+671, @CGUID+668, 0, 0, 3),
|
||||
(@CGUID+672, @CGUID+668, 0, 0, 3),
|
||||
(@CGUID+673, @CGUID+673, 0, 0, 3),
|
||||
(@CGUID+674, @CGUID+673, 0, 0, 3),
|
||||
(@CGUID+675, @CGUID+673, 0, 0, 3),
|
||||
(@CGUID+676, @CGUID+673, 0, 0, 3),
|
||||
(@CGUID+677, @CGUID+673, 0, 0, 3),
|
||||
(@CGUID+691, @CGUID+691, 0, 0, 3),
|
||||
(@CGUID+692, @CGUID+691, 0, 0, 3),
|
||||
(@CGUID+693, @CGUID+691, 0, 0, 3),
|
||||
(@CGUID+694, @CGUID+691, 0, 0, 3),
|
||||
(@CGUID+695, @CGUID+691, 0, 0, 3),
|
||||
(@CGUID+696, @CGUID+691, 0, 0, 3),
|
||||
(@CGUID+697, @CGUID+697, 0, 0, 3),
|
||||
(@CGUID+698, @CGUID+697, 0, 0, 3),
|
||||
(@CGUID+699, @CGUID+697, 0, 0, 3),
|
||||
(@CGUID+700, @CGUID+697, 0, 0, 3),
|
||||
(@CGUID+701, @CGUID+697, 0, 0, 3),
|
||||
(@CGUID+702, @CGUID+697, 0, 0, 3),
|
||||
(@CGUID+703, @CGUID+703, 0, 0, 3),
|
||||
(@CGUID+704, @CGUID+703, 0, 0, 3),
|
||||
(@CGUID+705, @CGUID+703, 0, 0, 3),
|
||||
(@CGUID+706, @CGUID+703, 0, 0, 3),
|
||||
(@CGUID+707, @CGUID+703, 0, 0, 3),
|
||||
(@CGUID+708, @CGUID+703, 0, 0, 3),
|
||||
(@CGUID+709, @CGUID+709, 0, 0, 3),
|
||||
(@CGUID+710, @CGUID+709, 0, 0, 3),
|
||||
(@CGUID+711, @CGUID+709, 0, 0, 3),
|
||||
(@CGUID+712, @CGUID+709, 0, 0, 3),
|
||||
(@CGUID+713, @CGUID+709, 0, 0, 3),
|
||||
(@CGUID+714, @CGUID+709, 0, 0, 3),
|
||||
(@CGUID+716, @CGUID+716, 0, 0, 3),
|
||||
(@CGUID+717, @CGUID+716, 0, 0, 3),
|
||||
(@CGUID+718, @CGUID+716, 0, 0, 3),
|
||||
(@CGUID+719, @CGUID+716, 0, 0, 3);
|
||||
|
||||
DELETE FROM `creature_addon` WHERE `guid` IN (12726,12727,12728,12729,12730,12731,12732,12733,12734,12735,12736,12760,12776,12778,12779,12780,12781,12782,12784,12785,12786,12787,12788,12789,12790,12791,12792,12793,12794,12795,12796,12797,12798,12799,12800,12801,12802,12803,12804,12805,12806,12807,12808,12809,12810,12843,12866,12869,12870,12871,12872,12873,12874,12875,12876,12877,12878,12879,12880,12881,12882,12883,12884,12886,12888,12889,12892,12894,13230,13235,40446,40526,40527,42920,46817,52411,52418,52420,52423,52424,52427,52428,52429,52430,52431,52432,52433,52440,52441,52442,52443,52444,52445,52446,52447,52448,52449,52450,52451,52452,52453,52454,52455,52456,52457,52458,52459,52460,52461,52462,52463,52464,52465,52739,52740,52743,52768,52769,52772,52773,52846,52847,52848,52850,52854,52855,52857,53054,53055,53056,53057,53058,53059,53210,53229,53586,53710,53711,53816,84716);
|
||||
DELETE FROM `waypoint_data` WHERE `id` IN (128660,128690,128840,128860,128880,128890,404460,405260,429200,468170,524110,524180,524200,524230,524240,524270,527390,527400,527430,527680,527690,527720,527730,528460,528470,528480,528500,528540,528550,528570,530540,530550,530560,530570,530580,530590,538160);
|
||||
|
||||
@@ -18,14 +18,14 @@ INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Lan
|
||||
UPDATE `creature` SET `spawntimesecs` = 300 WHERE `id1` = 23191 AND `map` = 564;
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `leaderGUID` = 148236;
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `groupAI`) VALUES
|
||||
(148236, 148236, 24),
|
||||
(148237, 148236, 24),
|
||||
(148238, 148236, 24),
|
||||
(148239, 148236, 24),
|
||||
(148240, 148236, 24),
|
||||
(148241, 148236, 24),
|
||||
(148242, 148236, 24);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(148236, 148236, 0, 0, 24),
|
||||
(148237, 148236, 0, 0, 24),
|
||||
(148238, 148236, 0, 0, 24),
|
||||
(148239, 148236, 0, 0, 24),
|
||||
(148240, 148236, 0, 0, 24),
|
||||
(148241, 148236, 0, 0, 24),
|
||||
(148242, 148236, 0, 0, 24);
|
||||
|
||||
-- Delete leftover gobs
|
||||
DELETE FROM `gameobject` WHERE `guid` IN (20523,20558,20559,20561,20563,20567) AND `map` = 564;
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
-- DB update 2024_07_09_06 -> 2024_07_09_07
|
||||
--
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (158167,158168,158169,158170,158171,158172,158173,158174,158175,158176,158177,158178,158155,158156,158157,158158,158159,158160,158161,158162,158163,158164,158165,158166,158143,158144,158145,158146,158147,158148,158149,158150,158151,158152,158153,158154);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(158167, 158167, 3),
|
||||
(158167, 158168, 3),
|
||||
(158167, 158169, 3),
|
||||
(158167, 158170, 3),
|
||||
(158167, 158171, 3),
|
||||
(158167, 158172, 3),
|
||||
(158167, 158173, 3),
|
||||
(158167, 158174, 3),
|
||||
(158167, 158175, 3),
|
||||
(158167, 158176, 3),
|
||||
(158167, 158177, 3),
|
||||
(158167, 158178, 3),
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(158167, 158167, 0, 0, 3),
|
||||
(158167, 158168, 0, 0, 3),
|
||||
(158167, 158169, 0, 0, 3),
|
||||
(158167, 158170, 0, 0, 3),
|
||||
(158167, 158171, 0, 0, 3),
|
||||
(158167, 158172, 0, 0, 3),
|
||||
(158167, 158173, 0, 0, 3),
|
||||
(158167, 158174, 0, 0, 3),
|
||||
(158167, 158175, 0, 0, 3),
|
||||
(158167, 158176, 0, 0, 3),
|
||||
(158167, 158177, 0, 0, 3),
|
||||
(158167, 158178, 0, 0, 3),
|
||||
|
||||
(158155, 158155, 3),
|
||||
(158155, 158156, 3),
|
||||
(158155, 158157, 3),
|
||||
(158155, 158158, 3),
|
||||
(158155, 158159, 3),
|
||||
(158155, 158160, 3),
|
||||
(158155, 158161, 3),
|
||||
(158155, 158162, 3),
|
||||
(158155, 158163, 3),
|
||||
(158155, 158164, 3),
|
||||
(158155, 158165, 3),
|
||||
(158155, 158166, 3),
|
||||
(158155, 158155, 0, 0, 3),
|
||||
(158155, 158156, 0, 0, 3),
|
||||
(158155, 158157, 0, 0, 3),
|
||||
(158155, 158158, 0, 0, 3),
|
||||
(158155, 158159, 0, 0, 3),
|
||||
(158155, 158160, 0, 0, 3),
|
||||
(158155, 158161, 0, 0, 3),
|
||||
(158155, 158162, 0, 0, 3),
|
||||
(158155, 158163, 0, 0, 3),
|
||||
(158155, 158164, 0, 0, 3),
|
||||
(158155, 158165, 0, 0, 3),
|
||||
(158155, 158166, 0, 0, 3),
|
||||
|
||||
(158143, 158143, 3),
|
||||
(158143, 158144, 3),
|
||||
(158143, 158145, 3),
|
||||
(158143, 158146, 3),
|
||||
(158143, 158147, 3),
|
||||
(158143, 158148, 3),
|
||||
(158143, 158149, 3),
|
||||
(158143, 158150, 3),
|
||||
(158143, 158151, 3),
|
||||
(158143, 158152, 3),
|
||||
(158143, 158153, 3),
|
||||
(158143, 158154, 3);
|
||||
(158143, 158143, 0, 0, 3),
|
||||
(158143, 158144, 0, 0, 3),
|
||||
(158143, 158145, 0, 0, 3),
|
||||
(158143, 158146, 0, 0, 3),
|
||||
(158143, 158147, 0, 0, 3),
|
||||
(158143, 158148, 0, 0, 3),
|
||||
(158143, 158149, 0, 0, 3),
|
||||
(158143, 158150, 0, 0, 3),
|
||||
(158143, 158151, 0, 0, 3),
|
||||
(158143, 158152, 0, 0, 3),
|
||||
(158143, 158153, 0, 0, 3),
|
||||
(158143, 158154, 0, 0, 3);
|
||||
|
||||
@@ -42,11 +42,26 @@ CREATE TABLE `realmlist` (
|
||||
--
|
||||
-- Dumping data for table `realmlist`
|
||||
--
|
||||
-- Defaults are tuned for fresh local installs: `address` is what the auth
|
||||
-- server hands clients after login as the WORLD server endpoint. Stock
|
||||
-- 127.0.0.1 means "the same box auth is running on", so a fresh
|
||||
-- `git clone` -> `docker compose up` works without any post-install
|
||||
-- tweaks for a developer hosting on their own machine.
|
||||
--
|
||||
-- Production deployments must override `address` after first dbimport,
|
||||
-- e.g.:
|
||||
-- UPDATE realmlist SET address = 'your.public.host', port = 8085 WHERE id = 1;
|
||||
-- See contrib/fractured-dev-extras/BUILD-NATIVE.md for the full deploy
|
||||
-- checklist (auth/world ports, firewall, public hostnames).
|
||||
--
|
||||
-- `port` is the WORLD server port (must match WorldServerPort in
|
||||
-- worldserver.conf). The auth-server LISTEN port is separately configured
|
||||
-- via RealmServerPort in authserver.conf (stock default 3724).
|
||||
|
||||
LOCK TABLES `realmlist` WRITE;
|
||||
/*!40000 ALTER TABLE `realmlist` DISABLE KEYS */;
|
||||
INSERT INTO `realmlist` VALUES
|
||||
(1,'AzerothCore','127.0.0.1','127.0.0.1','255.255.255.0',8085,0,0,1,0,0,12340);
|
||||
(1,'Fractured WoW','127.0.0.1','127.0.0.1','255.255.255.0',8085,0,0,1,0,0,12340);
|
||||
/*!40000 ALTER TABLE `realmlist` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ CREATE TABLE `world_state` (
|
||||
`Id` int unsigned NOT NULL COMMENT 'Internal save ID',
|
||||
`Data` longtext,
|
||||
PRIMARY KEY (`Id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='WorldState save system';
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='WorldState save system';
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
|
||||
@@ -27,7 +27,7 @@ CREATE TABLE `player_shapeshift_model` (
|
||||
`GenderID` tinyint unsigned NOT NULL,
|
||||
`ModelID` int unsigned NOT NULL,
|
||||
PRIMARY KEY (`ShapeshiftID`,`RaceID`,`CustomizationID`,`GenderID`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci PACK_KEYS=0;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci PACK_KEYS=0;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
|
||||
@@ -25,7 +25,7 @@ CREATE TABLE `player_totem_model` (
|
||||
`RaceID` tinyint unsigned NOT NULL,
|
||||
`ModelID` int unsigned NOT NULL,
|
||||
PRIMARY KEY (`TotemID`,`RaceID`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci PACK_KEYS=0;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci PACK_KEYS=0;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
|
||||
@@ -546,7 +546,7 @@ INSERT INTO `spell_target_position` VALUES
|
||||
(48274,0,575,302.36,-352.01,90.54,2.2,0),
|
||||
(48275,0,575,291.39,-352.01,90.54,0.91,0),
|
||||
(48276,0,575,296.651,-346.293,108.547,1.58,0),
|
||||
(48324,0,571,3454.11,-2802.37,202.14,0,34149345),
|
||||
(48324,0,571,3454.11,-2802.37,202.14,0,0),
|
||||
(48622,0,571,4274.53,-3055.55,319.463,2.535,0),
|
||||
(48760,0,571,3876.16,6984.44,106.32,6.279,0),
|
||||
(48960,0,571,4313.37,-2958.17,318.463,1.98,0),
|
||||
|
||||
@@ -665,97 +665,97 @@ INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`,
|
||||
|
||||
-- Static Linking
|
||||
DELETE FROM `creature_formations` WHERE `groupAI` IN (3, 27) AND `leaderGUID` IN (@CGUID+22,@CGUID+23,@CGUID+26,@CGUID+28,@CGUID+30,@CGUID+31,@CGUID+36,@CGUID+37,@CGUID+41,@CGUID+42,@CGUID+43,@CGUID+44,@CGUID+61,@CGUID+63,@CGUID+72,@CGUID+80,@CGUID+86,@CGUID+102,@CGUID+103,@CGUID+110,@CGUID+117,@CGUID+120,@CGUID+139,@CGUID+140) AND `memberGUID` IN (@CGUID+21,@CGUID+22,@CGUID+23,@CGUID+24,@CGUID+25,@CGUID+26,@CGUID+27,@CGUID+28,@CGUID+29,@CGUID+30,@CGUID+31,@CGUID+32,@CGUID+33,@CGUID+34,@CGUID+36,@CGUID+37,@CGUID+38,@CGUID+40,@CGUID+41,@CGUID+42,@CGUID+43,@CGUID+44,@CGUID+61,@CGUID+62,@CGUID+63,@CGUID+64,@CGUID+65,@CGUID+66,@CGUID+67,@CGUID+70,@CGUID+71,@CGUID+72,@CGUID+73,@CGUID+74,@CGUID+75,@CGUID+76,@CGUID+77,@CGUID+78,@CGUID+79,@CGUID+80,@CGUID+82,@CGUID+83,@CGUID+84,@CGUID+86,@CGUID+87,@CGUID+89,@CGUID+90,@CGUID+91,@CGUID+92,@CGUID+93,@CGUID+94,@CGUID+95,@CGUID+96,@CGUID+98,@CGUID+99,@CGUID+100,@CGUID+101,@CGUID+102,@CGUID+103,@CGUID+106,@CGUID+110,@CGUID+111,@CGUID+112,@CGUID+113,@CGUID+114,@CGUID+115,@CGUID+116,@CGUID+117,@CGUID+118,@CGUID+119,@CGUID+120,@CGUID+122,@CGUID+124,@CGUID+125,@CGUID+126,@CGUID+127,@CGUID+128,@CGUID+139,@CGUID+140,@CGUID+141,@CGUID+142,@CGUID+143,@CGUID+144,@CGUID+145,@CGUID+146,@CGUID+147,@CGUID+148,@CGUID+149,@CGUID+150,@CGUID+151,@CGUID+152,@CGUID+153,@CGUID+154);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(@CGUID+103, @CGUID+103, 3),
|
||||
(@CGUID+103, @CGUID+21, 3),
|
||||
(@CGUID+103, @CGUID+25, 3),
|
||||
(@CGUID+30, @CGUID+30, 3),
|
||||
(@CGUID+30, @CGUID+95, 3),
|
||||
(@CGUID+30, @CGUID+114, 3),
|
||||
(@CGUID+30, @CGUID+83, 3),
|
||||
(@CGUID+110, @CGUID+110, 3),
|
||||
(@CGUID+110, @CGUID+77, 3),
|
||||
(@CGUID+110, @CGUID+79, 3),
|
||||
(@CGUID+22, @CGUID+22, 3),
|
||||
(@CGUID+22, @CGUID+84, 3),
|
||||
(@CGUID+22, @CGUID+99, 3),
|
||||
(@CGUID+26, @CGUID+26, 3),
|
||||
(@CGUID+26, @CGUID+96, 3),
|
||||
(@CGUID+26, @CGUID+112, 3),
|
||||
(@CGUID+26, @CGUID+27, 3),
|
||||
(@CGUID+28, @CGUID+28, 3),
|
||||
(@CGUID+28, @CGUID+29, 3),
|
||||
(@CGUID+28, @CGUID+100, 3),
|
||||
(@CGUID+28, @CGUID+111, 3),
|
||||
(@CGUID+23, @CGUID+23, 3),
|
||||
(@CGUID+23, @CGUID+78, 3),
|
||||
(@CGUID+23, @CGUID+101, 3),
|
||||
(@CGUID+23, @CGUID+24, 3),
|
||||
(@CGUID+86, @CGUID+86, 3),
|
||||
(@CGUID+86, @CGUID+87, 3),
|
||||
(@CGUID+86, @CGUID+115, 3),
|
||||
(@CGUID+86, @CGUID+106, 3),
|
||||
(@CGUID+80, @CGUID+80, 3),
|
||||
(@CGUID+80, @CGUID+98, 3),
|
||||
(@CGUID+80, @CGUID+82, 3),
|
||||
(@CGUID+80, @CGUID+113, 3),
|
||||
(@CGUID+44, @CGUID+44, 27),
|
||||
(@CGUID+44, @CGUID+124, 27),
|
||||
(@CGUID+44, @CGUID+125, 27),
|
||||
(@CGUID+61, @CGUID+61, 3),
|
||||
(@CGUID+61, @CGUID+62, 3),
|
||||
(@CGUID+102, @CGUID+102, 3),
|
||||
(@CGUID+102, @CGUID+33, 3),
|
||||
(@CGUID+102, @CGUID+34, 3),
|
||||
(@CGUID+37, @CGUID+37, 3),
|
||||
(@CGUID+37, @CGUID+91, 3),
|
||||
(@CGUID+37, @CGUID+67, 3),
|
||||
(@CGUID+37, @CGUID+66, 3),
|
||||
(@CGUID+36, @CGUID+36, 3),
|
||||
(@CGUID+36, @CGUID+90, 3),
|
||||
(@CGUID+36, @CGUID+89, 3),
|
||||
(@CGUID+36, @CGUID+64, 3),
|
||||
(@CGUID+31, @CGUID+31, 3),
|
||||
(@CGUID+31, @CGUID+32, 3),
|
||||
(@CGUID+31, @CGUID+38, 3),
|
||||
(@CGUID+31, @CGUID+70, 3),
|
||||
(@CGUID+63, @CGUID+63, 3),
|
||||
(@CGUID+63, @CGUID+65, 3),
|
||||
(@CGUID+120, @CGUID+120, 3),
|
||||
(@CGUID+120, @CGUID+122, 3),
|
||||
(@CGUID+139, @CGUID+139, 3),
|
||||
(@CGUID+139, @CGUID+146, 3),
|
||||
(@CGUID+139, @CGUID+143, 3),
|
||||
(@CGUID+139, @CGUID+145, 3),
|
||||
(@CGUID+139, @CGUID+147, 3),
|
||||
(@CGUID+139, @CGUID+149, 3),
|
||||
(@CGUID+139, @CGUID+154, 3),
|
||||
(@CGUID+139, @CGUID+144, 3),
|
||||
(@CGUID+140, @CGUID+140, 3),
|
||||
(@CGUID+140, @CGUID+152, 3),
|
||||
(@CGUID+140, @CGUID+142, 3),
|
||||
(@CGUID+140, @CGUID+150, 3),
|
||||
(@CGUID+140, @CGUID+153, 3),
|
||||
(@CGUID+140, @CGUID+151, 3),
|
||||
(@CGUID+140, @CGUID+141, 3),
|
||||
(@CGUID+140, @CGUID+148, 3),
|
||||
(@CGUID+43, @CGUID+43, 3),
|
||||
(@CGUID+43, @CGUID+127, 3),
|
||||
(@CGUID+43, @CGUID+126, 3),
|
||||
(@CGUID+43, @CGUID+128, 3),
|
||||
(@CGUID+72, @CGUID+72, 3),
|
||||
(@CGUID+72, @CGUID+116, 3),
|
||||
(@CGUID+72, @CGUID+71, 3),
|
||||
(@CGUID+72, @CGUID+40, 3),
|
||||
(@CGUID+117, @CGUID+117, 3),
|
||||
(@CGUID+117, @CGUID+74, 3),
|
||||
(@CGUID+117, @CGUID+73, 3),
|
||||
(@CGUID+117, @CGUID+92, 3),
|
||||
(@CGUID+41, @CGUID+41, 3),
|
||||
(@CGUID+41, @CGUID+75, 3),
|
||||
(@CGUID+41, @CGUID+118, 3),
|
||||
(@CGUID+41, @CGUID+93, 3),
|
||||
(@CGUID+42, @CGUID+42, 3),
|
||||
(@CGUID+42, @CGUID+76, 3),
|
||||
(@CGUID+42, @CGUID+94, 3),
|
||||
(@CGUID+42, @CGUID+119, 3);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(@CGUID+103, @CGUID+103, 0, 0, 3),
|
||||
(@CGUID+103, @CGUID+21, 0, 0, 3),
|
||||
(@CGUID+103, @CGUID+25, 0, 0, 3),
|
||||
(@CGUID+30, @CGUID+30, 0, 0, 3),
|
||||
(@CGUID+30, @CGUID+95, 0, 0, 3),
|
||||
(@CGUID+30, @CGUID+114, 0, 0, 3),
|
||||
(@CGUID+30, @CGUID+83, 0, 0, 3),
|
||||
(@CGUID+110, @CGUID+110, 0, 0, 3),
|
||||
(@CGUID+110, @CGUID+77, 0, 0, 3),
|
||||
(@CGUID+110, @CGUID+79, 0, 0, 3),
|
||||
(@CGUID+22, @CGUID+22, 0, 0, 3),
|
||||
(@CGUID+22, @CGUID+84, 0, 0, 3),
|
||||
(@CGUID+22, @CGUID+99, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+26, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+96, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+112, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+27, 0, 0, 3),
|
||||
(@CGUID+28, @CGUID+28, 0, 0, 3),
|
||||
(@CGUID+28, @CGUID+29, 0, 0, 3),
|
||||
(@CGUID+28, @CGUID+100, 0, 0, 3),
|
||||
(@CGUID+28, @CGUID+111, 0, 0, 3),
|
||||
(@CGUID+23, @CGUID+23, 0, 0, 3),
|
||||
(@CGUID+23, @CGUID+78, 0, 0, 3),
|
||||
(@CGUID+23, @CGUID+101, 0, 0, 3),
|
||||
(@CGUID+23, @CGUID+24, 0, 0, 3),
|
||||
(@CGUID+86, @CGUID+86, 0, 0, 3),
|
||||
(@CGUID+86, @CGUID+87, 0, 0, 3),
|
||||
(@CGUID+86, @CGUID+115, 0, 0, 3),
|
||||
(@CGUID+86, @CGUID+106, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+80, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+98, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+113, 0, 0, 3),
|
||||
(@CGUID+44, @CGUID+44, 0, 0, 27),
|
||||
(@CGUID+44, @CGUID+124, 0, 0, 27),
|
||||
(@CGUID+44, @CGUID+125, 0, 0, 27),
|
||||
(@CGUID+61, @CGUID+61, 0, 0, 3),
|
||||
(@CGUID+61, @CGUID+62, 0, 0, 3),
|
||||
(@CGUID+102, @CGUID+102, 0, 0, 3),
|
||||
(@CGUID+102, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+102, @CGUID+34, 0, 0, 3),
|
||||
(@CGUID+37, @CGUID+37, 0, 0, 3),
|
||||
(@CGUID+37, @CGUID+91, 0, 0, 3),
|
||||
(@CGUID+37, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+37, @CGUID+66, 0, 0, 3),
|
||||
(@CGUID+36, @CGUID+36, 0, 0, 3),
|
||||
(@CGUID+36, @CGUID+90, 0, 0, 3),
|
||||
(@CGUID+36, @CGUID+89, 0, 0, 3),
|
||||
(@CGUID+36, @CGUID+64, 0, 0, 3),
|
||||
(@CGUID+31, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+31, @CGUID+32, 0, 0, 3),
|
||||
(@CGUID+31, @CGUID+38, 0, 0, 3),
|
||||
(@CGUID+31, @CGUID+70, 0, 0, 3),
|
||||
(@CGUID+63, @CGUID+63, 0, 0, 3),
|
||||
(@CGUID+63, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+120, @CGUID+120, 0, 0, 3),
|
||||
(@CGUID+120, @CGUID+122, 0, 0, 3),
|
||||
(@CGUID+139, @CGUID+139, 0, 0, 3),
|
||||
(@CGUID+139, @CGUID+146, 0, 0, 3),
|
||||
(@CGUID+139, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+139, @CGUID+145, 0, 0, 3),
|
||||
(@CGUID+139, @CGUID+147, 0, 0, 3),
|
||||
(@CGUID+139, @CGUID+149, 0, 0, 3),
|
||||
(@CGUID+139, @CGUID+154, 0, 0, 3),
|
||||
(@CGUID+139, @CGUID+144, 0, 0, 3),
|
||||
(@CGUID+140, @CGUID+140, 0, 0, 3),
|
||||
(@CGUID+140, @CGUID+152, 0, 0, 3),
|
||||
(@CGUID+140, @CGUID+142, 0, 0, 3),
|
||||
(@CGUID+140, @CGUID+150, 0, 0, 3),
|
||||
(@CGUID+140, @CGUID+153, 0, 0, 3),
|
||||
(@CGUID+140, @CGUID+151, 0, 0, 3),
|
||||
(@CGUID+140, @CGUID+141, 0, 0, 3),
|
||||
(@CGUID+140, @CGUID+148, 0, 0, 3),
|
||||
(@CGUID+43, @CGUID+43, 0, 0, 3),
|
||||
(@CGUID+43, @CGUID+127, 0, 0, 3),
|
||||
(@CGUID+43, @CGUID+126, 0, 0, 3),
|
||||
(@CGUID+43, @CGUID+128, 0, 0, 3),
|
||||
(@CGUID+72, @CGUID+72, 0, 0, 3),
|
||||
(@CGUID+72, @CGUID+116, 0, 0, 3),
|
||||
(@CGUID+72, @CGUID+71, 0, 0, 3),
|
||||
(@CGUID+72, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+117, @CGUID+117, 0, 0, 3),
|
||||
(@CGUID+117, @CGUID+74, 0, 0, 3),
|
||||
(@CGUID+117, @CGUID+73, 0, 0, 3),
|
||||
(@CGUID+117, @CGUID+92, 0, 0, 3),
|
||||
(@CGUID+41, @CGUID+41, 0, 0, 3),
|
||||
(@CGUID+41, @CGUID+75, 0, 0, 3),
|
||||
(@CGUID+41, @CGUID+118, 0, 0, 3),
|
||||
(@CGUID+41, @CGUID+93, 0, 0, 3),
|
||||
(@CGUID+42, @CGUID+42, 0, 0, 3),
|
||||
(@CGUID+42, @CGUID+76, 0, 0, 3),
|
||||
(@CGUID+42, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+42, @CGUID+119, 0, 0, 3);
|
||||
|
||||
@@ -1239,162 +1239,162 @@ INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_
|
||||
*/
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `groupAI` = 3 AND `leaderGUID` IN (@CGUID+28,@CGUID+29,@CGUID+34,@CGUID+36,@CGUID+39,@CGUID+43,@CGUID+44,@CGUID+45,@CGUID+50,@CGUID+52,@CGUID+53,@CGUID+58,@CGUID+66,@CGUID+68,@CGUID+80,@CGUID+81,@CGUID+89,@CGUID+91,@CGUID+113,@CGUID+116,@CGUID+121,@CGUID+122,@CGUID+125,@CGUID+126,@CGUID+136,@CGUID+141,@CGUID+159,@CGUID+164,@CGUID+183,@CGUID+186,@CGUID+206,@CGUID+211,@CGUID+213,@CGUID+217,@CGUID+221,@CGUID+26) AND `memberGUID` IN (@CGUID+50,@CGUID+70,@CGUID+71,@CGUID+52,@CGUID+73,@CGUID+89,@CGUID+99,@CGUID+97,@CGUID+96,@CGUID+95,@CGUID+93,@CGUID+68,@CGUID+69,@CGUID+66,@CGUID+47,@CGUID+46,@CGUID+43,@CGUID+64,@CGUID+45,@CGUID+65,@CGUID+39,@CGUID+88,@CGUID+94,@CGUID+60,@CGUID+58,@CGUID+37,@CGUID+38,@CGUID+116,@CGUID+114,@CGUID+117,@CGUID+231,@CGUID+121,@CGUID+119,@CGUID+120,@CGUID+233,@CGUID+113,@CGUID+118,@CGUID+115,@CGUID+230,@CGUID+122,@CGUID+124,@CGUID+123,@CGUID+234,@CGUID+125,@CGUID+127,@CGUID+128,@CGUID+235,@CGUID+126,@CGUID+129,@CGUID+130,@CGUID+232,@CGUID+44,@CGUID+79,@CGUID+63,@CGUID+31,@CGUID+80,@CGUID+41,@CGUID+62,@CGUID+42,@CGUID+32,@CGUID+81,@CGUID+49,@CGUID+33,@CGUID+48,@CGUID+67,@CGUID+34,@CGUID+82,@CGUID+72,@CGUID+51,@CGUID+102,@CGUID+92,@CGUID+53,@CGUID+35,@CGUID+91,@CGUID+101,@CGUID+40,@CGUID+78,@CGUID+61,@CGUID+30,@CGUID+29,@CGUID+57,@CGUID+186,@CGUID+188,@CGUID+187,@CGUID+185,@CGUID+36,@CGUID+55,@CGUID+56,@CGUID+28,@CGUID+54,@CGUID+183,@CGUID+181,@CGUID+182,@CGUID+180,@CGUID+159,@CGUID+158,@CGUID+161,@CGUID+165,@CGUID+166,@CGUID+177,@CGUID+172,@CGUID+176,@CGUID+168,@CGUID+160,@CGUID+164,@CGUID+163,@CGUID+162,@CGUID+167,@CGUID+169,@CGUID+175,@CGUID+173,@CGUID+170,@CGUID+171,@CGUID+174,@CGUID+136,@CGUID+149,@CGUID+145,@CGUID+152,@CGUID+155,@CGUID+153,@CGUID+144,@CGUID+140,@CGUID+137,@CGUID+147,@CGUID+141,@CGUID+150,@CGUID+154,@CGUID+151,@CGUID+146,@CGUID+142,@CGUID+148,@CGUID+143,@CGUID+138,@CGUID+139,@CGUID+211,@CGUID+212,@CGUID+213,@CGUID+214,@CGUID+215,@CGUID+216,@CGUID+217,@CGUID+218,@CGUID+219,@CGUID+220,@CGUID+221,@CGUID+222,@CGUID+223,@CGUID+224,@CGUID+206,@CGUID+207,@CGUID+208,@CGUID+26,@CGUID+191,@CGUID+195,@CGUID+85);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(@CGUID+50, @CGUID+50, 3),
|
||||
(@CGUID+50, @CGUID+70, 3),
|
||||
(@CGUID+50, @CGUID+71, 3),
|
||||
(@CGUID+52, @CGUID+52, 3),
|
||||
(@CGUID+52, @CGUID+73, 3),
|
||||
(@CGUID+89, @CGUID+89, 3),
|
||||
(@CGUID+89, @CGUID+99, 3),
|
||||
(@CGUID+89, @CGUID+97, 3),
|
||||
(@CGUID+89, @CGUID+96, 3),
|
||||
(@CGUID+89, @CGUID+95, 3),
|
||||
(@CGUID+89, @CGUID+93, 3),
|
||||
(@CGUID+68, @CGUID+68, 3),
|
||||
(@CGUID+68, @CGUID+69, 3),
|
||||
(@CGUID+66, @CGUID+66, 3),
|
||||
(@CGUID+66, @CGUID+47, 3),
|
||||
(@CGUID+66, @CGUID+46, 3),
|
||||
(@CGUID+43, @CGUID+43, 3),
|
||||
(@CGUID+43, @CGUID+64, 3),
|
||||
(@CGUID+45, @CGUID+45, 3),
|
||||
(@CGUID+45, @CGUID+65, 3),
|
||||
(@CGUID+39, @CGUID+39, 3),
|
||||
(@CGUID+39, @CGUID+88, 3),
|
||||
(@CGUID+39, @CGUID+94, 3),
|
||||
(@CGUID+39, @CGUID+60, 3),
|
||||
(@CGUID+58, @CGUID+58, 3),
|
||||
(@CGUID+58, @CGUID+37, 3),
|
||||
(@CGUID+58, @CGUID+38, 3),
|
||||
(@CGUID+116, @CGUID+116, 3),
|
||||
(@CGUID+116, @CGUID+114, 3),
|
||||
(@CGUID+116, @CGUID+117, 3),
|
||||
(@CGUID+116, @CGUID+231, 3),
|
||||
(@CGUID+121, @CGUID+121, 3),
|
||||
(@CGUID+121, @CGUID+119, 3),
|
||||
(@CGUID+121, @CGUID+120, 3),
|
||||
(@CGUID+121, @CGUID+233, 3),
|
||||
(@CGUID+113, @CGUID+113, 3),
|
||||
(@CGUID+113, @CGUID+118, 3),
|
||||
(@CGUID+113, @CGUID+115, 3),
|
||||
(@CGUID+113, @CGUID+230, 3),
|
||||
(@CGUID+122, @CGUID+122, 3),
|
||||
(@CGUID+122, @CGUID+124, 3),
|
||||
(@CGUID+122, @CGUID+123, 3),
|
||||
(@CGUID+122, @CGUID+234, 3),
|
||||
(@CGUID+125, @CGUID+125, 3),
|
||||
(@CGUID+125, @CGUID+127, 3),
|
||||
(@CGUID+125, @CGUID+128, 3),
|
||||
(@CGUID+125, @CGUID+235, 3),
|
||||
(@CGUID+126, @CGUID+126, 3),
|
||||
(@CGUID+126, @CGUID+129, 3),
|
||||
(@CGUID+126, @CGUID+130, 3),
|
||||
(@CGUID+126, @CGUID+232, 3),
|
||||
(@CGUID+44, @CGUID+44, 3),
|
||||
(@CGUID+44, @CGUID+79, 3),
|
||||
(@CGUID+44, @CGUID+63, 3),
|
||||
(@CGUID+44, @CGUID+31, 3),
|
||||
(@CGUID+80, @CGUID+80, 3),
|
||||
(@CGUID+80, @CGUID+41, 3),
|
||||
(@CGUID+80, @CGUID+62, 3),
|
||||
(@CGUID+80, @CGUID+42, 3),
|
||||
(@CGUID+80, @CGUID+32, 3),
|
||||
(@CGUID+81, @CGUID+81, 3),
|
||||
(@CGUID+81, @CGUID+49, 3),
|
||||
(@CGUID+81, @CGUID+33, 3),
|
||||
(@CGUID+81, @CGUID+48, 3),
|
||||
(@CGUID+81, @CGUID+67, 3),
|
||||
(@CGUID+34, @CGUID+34, 3),
|
||||
(@CGUID+34, @CGUID+82, 3),
|
||||
(@CGUID+34, @CGUID+72, 3),
|
||||
(@CGUID+34, @CGUID+51, 3),
|
||||
(@CGUID+34, @CGUID+102, 3),
|
||||
(@CGUID+34, @CGUID+92, 3),
|
||||
(@CGUID+53, @CGUID+53, 3),
|
||||
(@CGUID+53, @CGUID+35, 3),
|
||||
(@CGUID+91, @CGUID+91, 3),
|
||||
(@CGUID+91, @CGUID+101, 3),
|
||||
(@CGUID+91, @CGUID+40, 3),
|
||||
(@CGUID+91, @CGUID+78, 3),
|
||||
(@CGUID+91, @CGUID+61, 3),
|
||||
(@CGUID+91, @CGUID+30, 3),
|
||||
(@CGUID+29, @CGUID+29, 3),
|
||||
(@CGUID+29, @CGUID+57, 3),
|
||||
(@CGUID+186, @CGUID+186, 3),
|
||||
(@CGUID+186, @CGUID+188, 3),
|
||||
(@CGUID+186, @CGUID+187, 3),
|
||||
(@CGUID+186, @CGUID+185, 3),
|
||||
(@CGUID+36, @CGUID+36, 3),
|
||||
(@CGUID+36, @CGUID+55, 3),
|
||||
(@CGUID+36, @CGUID+56, 3),
|
||||
(@CGUID+28, @CGUID+28, 3),
|
||||
(@CGUID+28, @CGUID+54, 3),
|
||||
(@CGUID+183, @CGUID+183, 3),
|
||||
(@CGUID+183, @CGUID+181, 3),
|
||||
(@CGUID+183, @CGUID+182, 3),
|
||||
(@CGUID+183, @CGUID+180, 3),
|
||||
(@CGUID+159, @CGUID+159, 3),
|
||||
(@CGUID+159, @CGUID+158, 3),
|
||||
(@CGUID+159, @CGUID+161, 3),
|
||||
(@CGUID+159, @CGUID+165, 3),
|
||||
(@CGUID+159, @CGUID+166, 3),
|
||||
(@CGUID+159, @CGUID+177, 3),
|
||||
(@CGUID+159, @CGUID+172, 3),
|
||||
(@CGUID+159, @CGUID+176, 3),
|
||||
(@CGUID+159, @CGUID+168, 3),
|
||||
(@CGUID+159, @CGUID+160, 3),
|
||||
(@CGUID+164, @CGUID+164, 3),
|
||||
(@CGUID+164, @CGUID+163, 3),
|
||||
(@CGUID+164, @CGUID+162, 3),
|
||||
(@CGUID+164, @CGUID+167, 3),
|
||||
(@CGUID+164, @CGUID+169, 3),
|
||||
(@CGUID+164, @CGUID+175, 3),
|
||||
(@CGUID+164, @CGUID+173, 3),
|
||||
(@CGUID+164, @CGUID+170, 3),
|
||||
(@CGUID+164, @CGUID+171, 3),
|
||||
(@CGUID+164, @CGUID+174, 3),
|
||||
(@CGUID+136, @CGUID+136, 3),
|
||||
(@CGUID+136, @CGUID+149, 3),
|
||||
(@CGUID+136, @CGUID+145, 3),
|
||||
(@CGUID+136, @CGUID+152, 3),
|
||||
(@CGUID+136, @CGUID+155, 3),
|
||||
(@CGUID+136, @CGUID+153, 3),
|
||||
(@CGUID+136, @CGUID+144, 3),
|
||||
(@CGUID+136, @CGUID+140, 3),
|
||||
(@CGUID+136, @CGUID+137, 3),
|
||||
(@CGUID+136, @CGUID+147, 3),
|
||||
(@CGUID+141, @CGUID+141, 3),
|
||||
(@CGUID+141, @CGUID+150, 3),
|
||||
(@CGUID+141, @CGUID+154, 3),
|
||||
(@CGUID+141, @CGUID+151, 3),
|
||||
(@CGUID+141, @CGUID+146, 3),
|
||||
(@CGUID+141, @CGUID+142, 3),
|
||||
(@CGUID+141, @CGUID+148, 3),
|
||||
(@CGUID+141, @CGUID+143, 3),
|
||||
(@CGUID+141, @CGUID+138, 3),
|
||||
(@CGUID+141, @CGUID+139, 3),
|
||||
(@CGUID+211, @CGUID+211, 3),
|
||||
(@CGUID+211, @CGUID+212, 3),
|
||||
(@CGUID+213, @CGUID+213, 3),
|
||||
(@CGUID+213, @CGUID+214, 3),
|
||||
(@CGUID+213, @CGUID+215, 3),
|
||||
(@CGUID+213, @CGUID+216, 3),
|
||||
(@CGUID+217, @CGUID+217, 3),
|
||||
(@CGUID+217, @CGUID+218, 3),
|
||||
(@CGUID+217, @CGUID+219, 3),
|
||||
(@CGUID+217, @CGUID+220, 3),
|
||||
(@CGUID+221, @CGUID+221, 3),
|
||||
(@CGUID+221, @CGUID+222, 3),
|
||||
(@CGUID+221, @CGUID+223, 3),
|
||||
(@CGUID+221, @CGUID+224, 3),
|
||||
(@CGUID+206, @CGUID+206, 3),
|
||||
(@CGUID+206, @CGUID+207, 3),
|
||||
(@CGUID+206, @CGUID+208, 3),
|
||||
(@CGUID+26, @CGUID+26, 3),
|
||||
(@CGUID+26, @CGUID+191, 3),
|
||||
(@CGUID+26, @CGUID+195, 3),
|
||||
(@CGUID+26, @CGUID+85, 3);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(@CGUID+50, @CGUID+50, 0, 0, 3),
|
||||
(@CGUID+50, @CGUID+70, 0, 0, 3),
|
||||
(@CGUID+50, @CGUID+71, 0, 0, 3),
|
||||
(@CGUID+52, @CGUID+52, 0, 0, 3),
|
||||
(@CGUID+52, @CGUID+73, 0, 0, 3),
|
||||
(@CGUID+89, @CGUID+89, 0, 0, 3),
|
||||
(@CGUID+89, @CGUID+99, 0, 0, 3),
|
||||
(@CGUID+89, @CGUID+97, 0, 0, 3),
|
||||
(@CGUID+89, @CGUID+96, 0, 0, 3),
|
||||
(@CGUID+89, @CGUID+95, 0, 0, 3),
|
||||
(@CGUID+89, @CGUID+93, 0, 0, 3),
|
||||
(@CGUID+68, @CGUID+68, 0, 0, 3),
|
||||
(@CGUID+68, @CGUID+69, 0, 0, 3),
|
||||
(@CGUID+66, @CGUID+66, 0, 0, 3),
|
||||
(@CGUID+66, @CGUID+47, 0, 0, 3),
|
||||
(@CGUID+66, @CGUID+46, 0, 0, 3),
|
||||
(@CGUID+43, @CGUID+43, 0, 0, 3),
|
||||
(@CGUID+43, @CGUID+64, 0, 0, 3),
|
||||
(@CGUID+45, @CGUID+45, 0, 0, 3),
|
||||
(@CGUID+45, @CGUID+65, 0, 0, 3),
|
||||
(@CGUID+39, @CGUID+39, 0, 0, 3),
|
||||
(@CGUID+39, @CGUID+88, 0, 0, 3),
|
||||
(@CGUID+39, @CGUID+94, 0, 0, 3),
|
||||
(@CGUID+39, @CGUID+60, 0, 0, 3),
|
||||
(@CGUID+58, @CGUID+58, 0, 0, 3),
|
||||
(@CGUID+58, @CGUID+37, 0, 0, 3),
|
||||
(@CGUID+58, @CGUID+38, 0, 0, 3),
|
||||
(@CGUID+116, @CGUID+116, 0, 0, 3),
|
||||
(@CGUID+116, @CGUID+114, 0, 0, 3),
|
||||
(@CGUID+116, @CGUID+117, 0, 0, 3),
|
||||
(@CGUID+116, @CGUID+231, 0, 0, 3),
|
||||
(@CGUID+121, @CGUID+121, 0, 0, 3),
|
||||
(@CGUID+121, @CGUID+119, 0, 0, 3),
|
||||
(@CGUID+121, @CGUID+120, 0, 0, 3),
|
||||
(@CGUID+121, @CGUID+233, 0, 0, 3),
|
||||
(@CGUID+113, @CGUID+113, 0, 0, 3),
|
||||
(@CGUID+113, @CGUID+118, 0, 0, 3),
|
||||
(@CGUID+113, @CGUID+115, 0, 0, 3),
|
||||
(@CGUID+113, @CGUID+230, 0, 0, 3),
|
||||
(@CGUID+122, @CGUID+122, 0, 0, 3),
|
||||
(@CGUID+122, @CGUID+124, 0, 0, 3),
|
||||
(@CGUID+122, @CGUID+123, 0, 0, 3),
|
||||
(@CGUID+122, @CGUID+234, 0, 0, 3),
|
||||
(@CGUID+125, @CGUID+125, 0, 0, 3),
|
||||
(@CGUID+125, @CGUID+127, 0, 0, 3),
|
||||
(@CGUID+125, @CGUID+128, 0, 0, 3),
|
||||
(@CGUID+125, @CGUID+235, 0, 0, 3),
|
||||
(@CGUID+126, @CGUID+126, 0, 0, 3),
|
||||
(@CGUID+126, @CGUID+129, 0, 0, 3),
|
||||
(@CGUID+126, @CGUID+130, 0, 0, 3),
|
||||
(@CGUID+126, @CGUID+232, 0, 0, 3),
|
||||
(@CGUID+44, @CGUID+44, 0, 0, 3),
|
||||
(@CGUID+44, @CGUID+79, 0, 0, 3),
|
||||
(@CGUID+44, @CGUID+63, 0, 0, 3),
|
||||
(@CGUID+44, @CGUID+31, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+80, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+41, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+62, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+42, 0, 0, 3),
|
||||
(@CGUID+80, @CGUID+32, 0, 0, 3),
|
||||
(@CGUID+81, @CGUID+81, 0, 0, 3),
|
||||
(@CGUID+81, @CGUID+49, 0, 0, 3),
|
||||
(@CGUID+81, @CGUID+33, 0, 0, 3),
|
||||
(@CGUID+81, @CGUID+48, 0, 0, 3),
|
||||
(@CGUID+81, @CGUID+67, 0, 0, 3),
|
||||
(@CGUID+34, @CGUID+34, 0, 0, 3),
|
||||
(@CGUID+34, @CGUID+82, 0, 0, 3),
|
||||
(@CGUID+34, @CGUID+72, 0, 0, 3),
|
||||
(@CGUID+34, @CGUID+51, 0, 0, 3),
|
||||
(@CGUID+34, @CGUID+102, 0, 0, 3),
|
||||
(@CGUID+34, @CGUID+92, 0, 0, 3),
|
||||
(@CGUID+53, @CGUID+53, 0, 0, 3),
|
||||
(@CGUID+53, @CGUID+35, 0, 0, 3),
|
||||
(@CGUID+91, @CGUID+91, 0, 0, 3),
|
||||
(@CGUID+91, @CGUID+101, 0, 0, 3),
|
||||
(@CGUID+91, @CGUID+40, 0, 0, 3),
|
||||
(@CGUID+91, @CGUID+78, 0, 0, 3),
|
||||
(@CGUID+91, @CGUID+61, 0, 0, 3),
|
||||
(@CGUID+91, @CGUID+30, 0, 0, 3),
|
||||
(@CGUID+29, @CGUID+29, 0, 0, 3),
|
||||
(@CGUID+29, @CGUID+57, 0, 0, 3),
|
||||
(@CGUID+186, @CGUID+186, 0, 0, 3),
|
||||
(@CGUID+186, @CGUID+188, 0, 0, 3),
|
||||
(@CGUID+186, @CGUID+187, 0, 0, 3),
|
||||
(@CGUID+186, @CGUID+185, 0, 0, 3),
|
||||
(@CGUID+36, @CGUID+36, 0, 0, 3),
|
||||
(@CGUID+36, @CGUID+55, 0, 0, 3),
|
||||
(@CGUID+36, @CGUID+56, 0, 0, 3),
|
||||
(@CGUID+28, @CGUID+28, 0, 0, 3),
|
||||
(@CGUID+28, @CGUID+54, 0, 0, 3),
|
||||
(@CGUID+183, @CGUID+183, 0, 0, 3),
|
||||
(@CGUID+183, @CGUID+181, 0, 0, 3),
|
||||
(@CGUID+183, @CGUID+182, 0, 0, 3),
|
||||
(@CGUID+183, @CGUID+180, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+159, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+161, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+165, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+166, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+177, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+172, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+176, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+168, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+160, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+164, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+163, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+162, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+167, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+169, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+175, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+173, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+170, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+171, 0, 0, 3),
|
||||
(@CGUID+164, @CGUID+174, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+136, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+149, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+145, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+152, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+155, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+153, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+144, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+140, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+137, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+147, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+141, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+150, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+154, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+151, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+146, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+142, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+148, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+138, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+139, 0, 0, 3),
|
||||
(@CGUID+211, @CGUID+211, 0, 0, 3),
|
||||
(@CGUID+211, @CGUID+212, 0, 0, 3),
|
||||
(@CGUID+213, @CGUID+213, 0, 0, 3),
|
||||
(@CGUID+213, @CGUID+214, 0, 0, 3),
|
||||
(@CGUID+213, @CGUID+215, 0, 0, 3),
|
||||
(@CGUID+213, @CGUID+216, 0, 0, 3),
|
||||
(@CGUID+217, @CGUID+217, 0, 0, 3),
|
||||
(@CGUID+217, @CGUID+218, 0, 0, 3),
|
||||
(@CGUID+217, @CGUID+219, 0, 0, 3),
|
||||
(@CGUID+217, @CGUID+220, 0, 0, 3),
|
||||
(@CGUID+221, @CGUID+221, 0, 0, 3),
|
||||
(@CGUID+221, @CGUID+222, 0, 0, 3),
|
||||
(@CGUID+221, @CGUID+223, 0, 0, 3),
|
||||
(@CGUID+221, @CGUID+224, 0, 0, 3),
|
||||
(@CGUID+206, @CGUID+206, 0, 0, 3),
|
||||
(@CGUID+206, @CGUID+207, 0, 0, 3),
|
||||
(@CGUID+206, @CGUID+208, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+26, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+191, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+195, 0, 0, 3),
|
||||
(@CGUID+26, @CGUID+85, 0, 0, 3);
|
||||
|
||||
-- Linked Respawn for the respawning creatures in Murmur's Room
|
||||
DELETE FROM `linked_respawn` WHERE `guid` IN (@CGUID+225, @CGUID+226, @CGUID+227, @CGUID+228, @CGUID+229) AND `linkType`=0;
|
||||
|
||||
@@ -1395,90 +1395,90 @@ INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`,
|
||||
(@CGUID+180, @CGUID+182, 3, 270, 515);
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (@CGUID+006,@CGUID+007,@CGUID+010,@CGUID+011,@CGUID+012,@CGUID+013,@CGUID+014,@CGUID+015,@CGUID+016,@CGUID+017,@CGUID+018,@CGUID+008,@CGUID+009,@CGUID+019,@CGUID+023,@CGUID+021,@CGUID+022,@CGUID+020,@CGUID+024,@CGUID+027,@CGUID+025,@CGUID+028,@CGUID+026,@CGUID+029,@CGUID+032,@CGUID+030,@CGUID+033,@CGUID+031,@CGUID+034,@CGUID+035,@CGUID+036,@CGUID+037,@CGUID+038,@CGUID+039,@CGUID+040,@CGUID+041,@CGUID+094,@CGUID+095,@CGUID+096,@CGUID+097,@CGUID+121,@CGUID+122,@CGUID+123,@CGUID+124,@CGUID+131,@CGUID+134,@CGUID+132,@CGUID+135,@CGUID+133,@CGUID+136,@CGUID+137,@CGUID+138,@CGUID+139,@CGUID+140,@CGUID+141,@CGUID+142,@CGUID+175,@CGUID+176,@CGUID+177,@CGUID+178,@CGUID+183,@CGUID+184,@CGUID+192,@CGUID+193,@CGUID+046,@CGUID+047,@CGUID+048,@CGUID+148,@CGUID+149,@CGUID+150,@CGUID+151,@CGUID+152,@CGUID+143,@CGUID+144,@CGUID+145,@CGUID+147,@CGUID+146,@CGUID+153,@CGUID+155,@CGUID+157,@CGUID+154,@CGUID+156,@CGUID+158,@CGUID+159,@CGUID+160,@CGUID+161,@CGUID+162);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `groupAI`) VALUES
|
||||
(@CGUID+006,@CGUID+006,3),
|
||||
(@CGUID+007,@CGUID+006,3),
|
||||
(@CGUID+011,@CGUID+011,3),
|
||||
(@CGUID+012,@CGUID+011,3),
|
||||
(@CGUID+013,@CGUID+013,3),
|
||||
(@CGUID+014,@CGUID+013,3),
|
||||
(@CGUID+015,@CGUID+015,3),
|
||||
(@CGUID+016,@CGUID+015,3),
|
||||
(@CGUID+017,@CGUID+017,3),
|
||||
(@CGUID+018,@CGUID+017,3),
|
||||
(@CGUID+008,@CGUID+008,3),
|
||||
(@CGUID+009,@CGUID+008,3),
|
||||
(@CGUID+019,@CGUID+019,3),
|
||||
(@CGUID+023,@CGUID+019,3),
|
||||
(@CGUID+021,@CGUID+019,3),
|
||||
(@CGUID+022,@CGUID+019,3),
|
||||
(@CGUID+020,@CGUID+019,3),
|
||||
(@CGUID+024,@CGUID+024,3),
|
||||
(@CGUID+027,@CGUID+024,3),
|
||||
(@CGUID+025,@CGUID+024,3),
|
||||
(@CGUID+028,@CGUID+024,3),
|
||||
(@CGUID+026,@CGUID+024,3),
|
||||
(@CGUID+029,@CGUID+029,3),
|
||||
(@CGUID+032,@CGUID+029,3),
|
||||
(@CGUID+030,@CGUID+029,3),
|
||||
(@CGUID+033,@CGUID+029,3),
|
||||
(@CGUID+031,@CGUID+029,3),
|
||||
(@CGUID+034,@CGUID+029,3),
|
||||
(@CGUID+035,@CGUID+035,3),
|
||||
(@CGUID+036,@CGUID+035,3),
|
||||
(@CGUID+037,@CGUID+035,3),
|
||||
(@CGUID+038,@CGUID+035,3),
|
||||
(@CGUID+039,@CGUID+035,3),
|
||||
(@CGUID+040,@CGUID+035,3),
|
||||
(@CGUID+041,@CGUID+035,3),
|
||||
(@CGUID+094,@CGUID+094,3),
|
||||
(@CGUID+095,@CGUID+094,3),
|
||||
(@CGUID+096,@CGUID+094,3),
|
||||
(@CGUID+097,@CGUID+094,3),
|
||||
(@CGUID+121,@CGUID+121,3),
|
||||
(@CGUID+122,@CGUID+121,3),
|
||||
(@CGUID+123,@CGUID+121,3),
|
||||
(@CGUID+124,@CGUID+121,3),
|
||||
(@CGUID+131,@CGUID+131,3),
|
||||
(@CGUID+134,@CGUID+131,3),
|
||||
(@CGUID+132,@CGUID+131,3),
|
||||
(@CGUID+135,@CGUID+131,3),
|
||||
(@CGUID+133,@CGUID+131,3),
|
||||
(@CGUID+136,@CGUID+131,3),
|
||||
(@CGUID+137,@CGUID+137,3),
|
||||
(@CGUID+138,@CGUID+137,3),
|
||||
(@CGUID+139,@CGUID+137,3),
|
||||
(@CGUID+140,@CGUID+137,3),
|
||||
(@CGUID+141,@CGUID+137,3),
|
||||
(@CGUID+142,@CGUID+137,3),
|
||||
(@CGUID+175,@CGUID+175,3),
|
||||
(@CGUID+176,@CGUID+175,3),
|
||||
(@CGUID+177,@CGUID+177,3),
|
||||
(@CGUID+178,@CGUID+177,3),
|
||||
(@CGUID+183,@CGUID+183,3),
|
||||
(@CGUID+184,@CGUID+183,3),
|
||||
(@CGUID+192,@CGUID+192,3),
|
||||
(@CGUID+193,@CGUID+192,3),
|
||||
(@CGUID+046,@CGUID+046,3),
|
||||
(@CGUID+047,@CGUID+046,3),
|
||||
(@CGUID+048,@CGUID+046,3),
|
||||
(@CGUID+148,@CGUID+148,3),
|
||||
(@CGUID+149,@CGUID+148,3),
|
||||
(@CGUID+150,@CGUID+148,3),
|
||||
(@CGUID+151,@CGUID+148,3),
|
||||
(@CGUID+152,@CGUID+148,3),
|
||||
(@CGUID+143,@CGUID+143,3),
|
||||
(@CGUID+144,@CGUID+143,3),
|
||||
(@CGUID+145,@CGUID+143,3),
|
||||
(@CGUID+147,@CGUID+143,3),
|
||||
(@CGUID+146,@CGUID+143,3),
|
||||
(@CGUID+153,@CGUID+153,3),
|
||||
(@CGUID+155,@CGUID+153,3),
|
||||
(@CGUID+157,@CGUID+153,3),
|
||||
(@CGUID+154,@CGUID+153,3),
|
||||
(@CGUID+156,@CGUID+153,3),
|
||||
(@CGUID+158,@CGUID+158,3),
|
||||
(@CGUID+159,@CGUID+158,3),
|
||||
(@CGUID+160,@CGUID+158,3),
|
||||
(@CGUID+161,@CGUID+158,3),
|
||||
(@CGUID+162,@CGUID+158,3);
|
||||
INSERT INTO `creature_formations` (`memberGUID`, `leaderGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(@CGUID+006, @CGUID+006, 0, 0, 3),
|
||||
(@CGUID+007, @CGUID+006, 0, 0, 3),
|
||||
(@CGUID+011, @CGUID+011, 0, 0, 3),
|
||||
(@CGUID+012, @CGUID+011, 0, 0, 3),
|
||||
(@CGUID+013, @CGUID+013, 0, 0, 3),
|
||||
(@CGUID+014, @CGUID+013, 0, 0, 3),
|
||||
(@CGUID+015, @CGUID+015, 0, 0, 3),
|
||||
(@CGUID+016, @CGUID+015, 0, 0, 3),
|
||||
(@CGUID+017, @CGUID+017, 0, 0, 3),
|
||||
(@CGUID+018, @CGUID+017, 0, 0, 3),
|
||||
(@CGUID+008, @CGUID+008, 0, 0, 3),
|
||||
(@CGUID+009, @CGUID+008, 0, 0, 3),
|
||||
(@CGUID+019, @CGUID+019, 0, 0, 3),
|
||||
(@CGUID+023, @CGUID+019, 0, 0, 3),
|
||||
(@CGUID+021, @CGUID+019, 0, 0, 3),
|
||||
(@CGUID+022, @CGUID+019, 0, 0, 3),
|
||||
(@CGUID+020, @CGUID+019, 0, 0, 3),
|
||||
(@CGUID+024, @CGUID+024, 0, 0, 3),
|
||||
(@CGUID+027, @CGUID+024, 0, 0, 3),
|
||||
(@CGUID+025, @CGUID+024, 0, 0, 3),
|
||||
(@CGUID+028, @CGUID+024, 0, 0, 3),
|
||||
(@CGUID+026, @CGUID+024, 0, 0, 3),
|
||||
(@CGUID+029, @CGUID+029, 0, 0, 3),
|
||||
(@CGUID+032, @CGUID+029, 0, 0, 3),
|
||||
(@CGUID+030, @CGUID+029, 0, 0, 3),
|
||||
(@CGUID+033, @CGUID+029, 0, 0, 3),
|
||||
(@CGUID+031, @CGUID+029, 0, 0, 3),
|
||||
(@CGUID+034, @CGUID+029, 0, 0, 3),
|
||||
(@CGUID+035, @CGUID+035, 0, 0, 3),
|
||||
(@CGUID+036, @CGUID+035, 0, 0, 3),
|
||||
(@CGUID+037, @CGUID+035, 0, 0, 3),
|
||||
(@CGUID+038, @CGUID+035, 0, 0, 3),
|
||||
(@CGUID+039, @CGUID+035, 0, 0, 3),
|
||||
(@CGUID+040, @CGUID+035, 0, 0, 3),
|
||||
(@CGUID+041, @CGUID+035, 0, 0, 3),
|
||||
(@CGUID+094, @CGUID+094, 0, 0, 3),
|
||||
(@CGUID+095, @CGUID+094, 0, 0, 3),
|
||||
(@CGUID+096, @CGUID+094, 0, 0, 3),
|
||||
(@CGUID+097, @CGUID+094, 0, 0, 3),
|
||||
(@CGUID+121, @CGUID+121, 0, 0, 3),
|
||||
(@CGUID+122, @CGUID+121, 0, 0, 3),
|
||||
(@CGUID+123, @CGUID+121, 0, 0, 3),
|
||||
(@CGUID+124, @CGUID+121, 0, 0, 3),
|
||||
(@CGUID+131, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+134, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+132, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+135, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+133, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+136, @CGUID+131, 0, 0, 3),
|
||||
(@CGUID+137, @CGUID+137, 0, 0, 3),
|
||||
(@CGUID+138, @CGUID+137, 0, 0, 3),
|
||||
(@CGUID+139, @CGUID+137, 0, 0, 3),
|
||||
(@CGUID+140, @CGUID+137, 0, 0, 3),
|
||||
(@CGUID+141, @CGUID+137, 0, 0, 3),
|
||||
(@CGUID+142, @CGUID+137, 0, 0, 3),
|
||||
(@CGUID+175, @CGUID+175, 0, 0, 3),
|
||||
(@CGUID+176, @CGUID+175, 0, 0, 3),
|
||||
(@CGUID+177, @CGUID+177, 0, 0, 3),
|
||||
(@CGUID+178, @CGUID+177, 0, 0, 3),
|
||||
(@CGUID+183, @CGUID+183, 0, 0, 3),
|
||||
(@CGUID+184, @CGUID+183, 0, 0, 3),
|
||||
(@CGUID+192, @CGUID+192, 0, 0, 3),
|
||||
(@CGUID+193, @CGUID+192, 0, 0, 3),
|
||||
(@CGUID+046, @CGUID+046, 0, 0, 3),
|
||||
(@CGUID+047, @CGUID+046, 0, 0, 3),
|
||||
(@CGUID+048, @CGUID+046, 0, 0, 3),
|
||||
(@CGUID+148, @CGUID+148, 0, 0, 3),
|
||||
(@CGUID+149, @CGUID+148, 0, 0, 3),
|
||||
(@CGUID+150, @CGUID+148, 0, 0, 3),
|
||||
(@CGUID+151, @CGUID+148, 0, 0, 3),
|
||||
(@CGUID+152, @CGUID+148, 0, 0, 3),
|
||||
(@CGUID+143, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+144, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+145, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+147, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+146, @CGUID+143, 0, 0, 3),
|
||||
(@CGUID+153, @CGUID+153, 0, 0, 3),
|
||||
(@CGUID+155, @CGUID+153, 0, 0, 3),
|
||||
(@CGUID+157, @CGUID+153, 0, 0, 3),
|
||||
(@CGUID+154, @CGUID+153, 0, 0, 3),
|
||||
(@CGUID+156, @CGUID+153, 0, 0, 3),
|
||||
(@CGUID+158, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+159, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+160, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+161, @CGUID+158, 0, 0, 3),
|
||||
(@CGUID+162, @CGUID+158, 0, 0, 3);
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Clamp VerifiedBuild literals to signed MEDIUMINT range for DBs that still use
|
||||
MEDIUMINT on VerifiedBuild (see data/sql/old/db_world/8.x/2022_10_30_01.sql).
|
||||
|
||||
Processes INSERT (explicit column lists) and simple UPDATE ... VerifiedBuild = N.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
MAX_SIGNED_MEDIUMINT = 8388607
|
||||
MIN_SIGNED_MEDIUMINT = -8388608
|
||||
|
||||
INSERT_RE = re.compile(
|
||||
r"INSERT\s+INTO\s+`(?P<table>\w+)`\s*\((?P<cols>[^)]+)\)\s*VALUES\s*",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
# Base dumps: full row order ends with VerifiedBuild (6- or 8-column layouts).
|
||||
INSERT_SPELL_TP_VALUES_RE = re.compile(
|
||||
r"INSERT\s+INTO\s+`spell_target_position`\s*VALUES\s*",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
STMT_START = (
|
||||
"INSERT ",
|
||||
"DELETE ",
|
||||
"UPDATE ",
|
||||
"ALTER ",
|
||||
"DROP ",
|
||||
"CREATE ",
|
||||
"SET ",
|
||||
"REPLACE ",
|
||||
)
|
||||
|
||||
|
||||
def split_fields(inner: str) -> list[str]:
|
||||
fields: list[str] = []
|
||||
cur: list[str] = []
|
||||
depth = 0
|
||||
in_str = False
|
||||
str_ch = ""
|
||||
esc = False
|
||||
for ch in inner:
|
||||
if in_str:
|
||||
cur.append(ch)
|
||||
if esc:
|
||||
esc = False
|
||||
elif ch == "\\":
|
||||
esc = True
|
||||
elif ch == str_ch:
|
||||
in_str = False
|
||||
continue
|
||||
if ch in "'\"":
|
||||
in_str = True
|
||||
str_ch = ch
|
||||
cur.append(ch)
|
||||
continue
|
||||
if ch == "(":
|
||||
depth += 1
|
||||
elif ch == ")":
|
||||
depth -= 1
|
||||
if ch == "," and depth == 0:
|
||||
fields.append("".join(cur).strip())
|
||||
cur = []
|
||||
else:
|
||||
cur.append(ch)
|
||||
fields.append("".join(cur).strip())
|
||||
return fields
|
||||
|
||||
|
||||
def join_fields(fields: list[str]) -> str:
|
||||
return ", ".join(fields)
|
||||
|
||||
|
||||
def clamp_tuple(tuple_str: str, vb_idx: int) -> str:
|
||||
t = tuple_str.strip()
|
||||
if not (t.startswith("(") and t.endswith(")")):
|
||||
return tuple_str
|
||||
fields = split_fields(t[1:-1])
|
||||
if vb_idx >= len(fields):
|
||||
return tuple_str
|
||||
raw = fields[vb_idx].strip()
|
||||
if not re.fullmatch(r"-?\d+", raw):
|
||||
return tuple_str
|
||||
n = int(raw)
|
||||
if MIN_SIGNED_MEDIUMINT <= n <= MAX_SIGNED_MEDIUMINT:
|
||||
return tuple_str
|
||||
fields[vb_idx] = "0"
|
||||
return "(" + join_fields(fields) + ")"
|
||||
|
||||
|
||||
def process_value_segment(segment: str, vb_idx: int) -> tuple[str, int]:
|
||||
out: list[str] = []
|
||||
changes = 0
|
||||
i, n = 0, len(segment)
|
||||
while i < n:
|
||||
if segment[i] == "(":
|
||||
depth = 1
|
||||
j = i + 1
|
||||
while j < n and depth > 0:
|
||||
if segment[j] == "(":
|
||||
depth += 1
|
||||
elif segment[j] == ")":
|
||||
depth -= 1
|
||||
j += 1
|
||||
tup = segment[i:j]
|
||||
new_tup = clamp_tuple(tup, vb_idx)
|
||||
if new_tup != tup:
|
||||
changes += 1
|
||||
out.append(new_tup)
|
||||
i = j
|
||||
continue
|
||||
out.append(segment[i])
|
||||
i += 1
|
||||
return "".join(out), changes
|
||||
|
||||
|
||||
def clamp_update_verifiedbuild(line: str) -> tuple[str, int]:
|
||||
changes = 0
|
||||
|
||||
def repl(m: re.Match[str]) -> str:
|
||||
nonlocal changes
|
||||
num = m.group(1)
|
||||
n = int(num)
|
||||
if MIN_SIGNED_MEDIUMINT <= n <= MAX_SIGNED_MEDIUMINT:
|
||||
return m.group(0)
|
||||
changes += 1
|
||||
return f"`VerifiedBuild` = 0"
|
||||
|
||||
new_line = re.sub(
|
||||
r"`VerifiedBuild`\s*=\s*([0-9]+)",
|
||||
repl,
|
||||
line,
|
||||
flags=re.IGNORECASE,
|
||||
)
|
||||
return new_line, changes
|
||||
|
||||
|
||||
def first_tuple_vb_index(segment: str) -> int | None:
|
||||
i, n = 0, len(segment)
|
||||
while i < n:
|
||||
if segment[i] == "(":
|
||||
depth = 1
|
||||
j = i + 1
|
||||
while j < n and depth > 0:
|
||||
if segment[j] == "(":
|
||||
depth += 1
|
||||
elif segment[j] == ")":
|
||||
depth -= 1
|
||||
j += 1
|
||||
tup = segment[i:j]
|
||||
fields = split_fields(tup[1:-1])
|
||||
if fields:
|
||||
return len(fields) - 1
|
||||
return None
|
||||
i += 1
|
||||
return None
|
||||
|
||||
|
||||
def process_sql_text(text: str) -> tuple[str, int]:
|
||||
lines = text.splitlines(keepends=True)
|
||||
out: list[str] = []
|
||||
mode: str | None = None
|
||||
vb_idx = 0
|
||||
total_changes = 0
|
||||
|
||||
for line in lines:
|
||||
nl, ch = clamp_update_verifiedbuild(line)
|
||||
total_changes += ch
|
||||
line = nl
|
||||
|
||||
stripped = line.lstrip()
|
||||
|
||||
if mode == "insert_values":
|
||||
if stripped and not stripped.startswith("(") and not stripped.startswith("--"):
|
||||
if any(stripped.startswith(p) for p in STMT_START):
|
||||
mode = None
|
||||
|
||||
if mode == "spell_tp_values":
|
||||
if stripped and not stripped.startswith("(") and not stripped.startswith("--"):
|
||||
if any(stripped.startswith(p) for p in STMT_START):
|
||||
mode = None
|
||||
|
||||
if mode is None:
|
||||
m = INSERT_RE.search(line)
|
||||
if m and "`VerifiedBuild`" in m.group("cols"):
|
||||
cols = [c.strip().strip("`") for c in m.group("cols").split(",")]
|
||||
vb_idx = cols.index("VerifiedBuild")
|
||||
mode = "insert_values"
|
||||
prefix = line[: m.end()]
|
||||
suffix = line[m.end() :]
|
||||
if suffix.strip():
|
||||
new_suf, ch2 = process_value_segment(suffix, vb_idx)
|
||||
total_changes += ch2
|
||||
line = prefix + new_suf
|
||||
if ");" in line or line.rstrip().endswith(");"):
|
||||
mode = None
|
||||
out.append(line)
|
||||
continue
|
||||
m2 = INSERT_SPELL_TP_VALUES_RE.search(line)
|
||||
if m2:
|
||||
mode = "spell_tp_values"
|
||||
vb_idx = -1
|
||||
prefix = line[: m2.end()]
|
||||
suffix = line[m2.end() :]
|
||||
if suffix.strip():
|
||||
vb_idx = first_tuple_vb_index(suffix)
|
||||
if vb_idx is None:
|
||||
vb_idx = 7
|
||||
new_suf, ch2 = process_value_segment(suffix, vb_idx)
|
||||
total_changes += ch2
|
||||
line = prefix + new_suf
|
||||
if ");" in line or line.rstrip().endswith(");"):
|
||||
mode = None
|
||||
out.append(line)
|
||||
continue
|
||||
out.append(line)
|
||||
continue
|
||||
|
||||
if mode == "spell_tp_values":
|
||||
if vb_idx < 0:
|
||||
vb_idx = first_tuple_vb_index(line)
|
||||
if vb_idx is None:
|
||||
vb_idx = 7
|
||||
new_line, ch3 = process_value_segment(line, vb_idx)
|
||||
total_changes += ch3
|
||||
out.append(new_line)
|
||||
if ");" in line or line.rstrip().endswith(");"):
|
||||
mode = None
|
||||
continue
|
||||
|
||||
# insert_values continuation
|
||||
new_line, ch3 = process_value_segment(line, vb_idx)
|
||||
total_changes += ch3
|
||||
out.append(new_line)
|
||||
if ");" in line or line.rstrip().endswith(");"):
|
||||
mode = None
|
||||
|
||||
return "".join(out), total_changes
|
||||
|
||||
|
||||
def main() -> int:
|
||||
sql_root = Path(__file__).resolve().parents[1]
|
||||
roots = [
|
||||
sql_root / "archive" / "db_world",
|
||||
sql_root / "updates" / "db_world",
|
||||
sql_root / "base" / "db_world",
|
||||
]
|
||||
file_count = 0
|
||||
change_lines = 0
|
||||
for root in roots:
|
||||
if not root.exists():
|
||||
continue
|
||||
for path in sorted(root.glob("*.sql")):
|
||||
text = path.read_text(encoding="utf-8", errors="strict")
|
||||
new_text, ch = process_sql_text(text)
|
||||
if ch:
|
||||
path.write_text(new_text, encoding="utf-8")
|
||||
change_lines += ch
|
||||
file_count += 1
|
||||
print(f"{path.relative_to(sql_root)}: {ch} change(s)")
|
||||
print(f"Done. {file_count} file(s) written, {change_lines} change(s).")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -31,15 +31,15 @@ INSERT INTO `creature` (`guid`, `id1`, `map`, `zoneId`, `areaId`, `spawnMask`, `
|
||||
UPDATE `creature_template` SET `flags_extra` = `flags_extra`|33554432 WHERE `entry` IN (28732, 28733, 28734, 28731, 28730, 28729, 28684, 31612, 31616, 31615, 31617, 31608, 31605, 31606);
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `leaderGUID` IN (12758, 12759, 12760);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(12758, 12758, 11),
|
||||
(12758, 12762, 11),
|
||||
(12758, 12761, 11),
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(12758, 12758, 0, 0, 11),
|
||||
(12758, 12762, 0, 0, 11),
|
||||
(12758, 12761, 0, 0, 11),
|
||||
|
||||
(12759, 12759, 11),
|
||||
(12759, 12763, 11),
|
||||
(12759, 12764, 11),
|
||||
(12759, 12759, 0, 0, 11),
|
||||
(12759, 12763, 0, 0, 11),
|
||||
(12759, 12764, 0, 0, 11),
|
||||
|
||||
(12760, 12760, 11),
|
||||
(12760, 12765, 11),
|
||||
(12760, 12766, 11);
|
||||
(12760, 12760, 0, 0, 11),
|
||||
(12760, 12765, 0, 0, 11),
|
||||
(12760, 12766, 0, 0, 11);
|
||||
|
||||
@@ -19,37 +19,37 @@ INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_
|
||||
(16505, 0, 3, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 39, 15, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Naxxramas Follower - On Aggro - Call For Help');
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `memberGUID` IN (127800, 127987, 127988, 127989, 127990, 127991, 127992, 127993, 127994, 127995, 127996, 127997, 127998, 127999, 128000, 128001, 128019, 128020, 128021, 128022, 128023, 128024, 128025, 128026, 128027, 128028, 128029, 128030, 128031, 128032, 128033, 128034, 128035);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(127800, 127800, 1),
|
||||
(127800, 127987, 1),
|
||||
(127800, 127988, 1),
|
||||
(127800, 127989, 1),
|
||||
(127800, 127990, 1),
|
||||
(127800, 127991, 1),
|
||||
(127800, 127992, 1),
|
||||
(127800, 127993, 1),
|
||||
(127800, 127994, 1),
|
||||
(127800, 127995, 1),
|
||||
(127800, 127996, 1),
|
||||
(127800, 127997, 1),
|
||||
(127800, 127998, 1),
|
||||
(127800, 127999, 1),
|
||||
(127800, 128000, 1),
|
||||
(127800, 128001, 1),
|
||||
(127800, 128019, 1),
|
||||
(127800, 128020, 1),
|
||||
(127800, 128021, 1),
|
||||
(127800, 128022, 1),
|
||||
(127800, 128023, 1),
|
||||
(127800, 128024, 1),
|
||||
(127800, 128025, 1),
|
||||
(127800, 128026, 1),
|
||||
(127800, 128027, 1),
|
||||
(127800, 128028, 1),
|
||||
(127800, 128029, 1),
|
||||
(127800, 128030, 1),
|
||||
(127800, 128031, 1),
|
||||
(127800, 128032, 1),
|
||||
(127800, 128033, 1),
|
||||
(127800, 128034, 1),
|
||||
(127800, 128035, 1);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(127800, 127800, 0, 0, 1),
|
||||
(127800, 127987, 0, 0, 1),
|
||||
(127800, 127988, 0, 0, 1),
|
||||
(127800, 127989, 0, 0, 1),
|
||||
(127800, 127990, 0, 0, 1),
|
||||
(127800, 127991, 0, 0, 1),
|
||||
(127800, 127992, 0, 0, 1),
|
||||
(127800, 127993, 0, 0, 1),
|
||||
(127800, 127994, 0, 0, 1),
|
||||
(127800, 127995, 0, 0, 1),
|
||||
(127800, 127996, 0, 0, 1),
|
||||
(127800, 127997, 0, 0, 1),
|
||||
(127800, 127998, 0, 0, 1),
|
||||
(127800, 127999, 0, 0, 1),
|
||||
(127800, 128000, 0, 0, 1),
|
||||
(127800, 128001, 0, 0, 1),
|
||||
(127800, 128019, 0, 0, 1),
|
||||
(127800, 128020, 0, 0, 1),
|
||||
(127800, 128021, 0, 0, 1),
|
||||
(127800, 128022, 0, 0, 1),
|
||||
(127800, 128023, 0, 0, 1),
|
||||
(127800, 128024, 0, 0, 1),
|
||||
(127800, 128025, 0, 0, 1),
|
||||
(127800, 128026, 0, 0, 1),
|
||||
(127800, 128027, 0, 0, 1),
|
||||
(127800, 128028, 0, 0, 1),
|
||||
(127800, 128029, 0, 0, 1),
|
||||
(127800, 128030, 0, 0, 1),
|
||||
(127800, 128031, 0, 0, 1),
|
||||
(127800, 128032, 0, 0, 1),
|
||||
(127800, 128033, 0, 0, 1),
|
||||
(127800, 128034, 0, 0, 1),
|
||||
(127800, 128035, 0, 0, 1);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
-- DB update 2026_01_04_00 -> 2026_01_04_01
|
||||
--
|
||||
DELETE FROM `creature_formations` WHERE `leaderGUID` IN (12758, 12759, 12760);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(12758, 12758, 7),
|
||||
(12758, 12762, 7),
|
||||
(12758, 12761, 7),
|
||||
(12759, 12759, 7),
|
||||
(12759, 12763, 7),
|
||||
(12759, 12764, 7),
|
||||
(12760, 12760, 7),
|
||||
(12760, 12765, 7),
|
||||
(12760, 12766, 7);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(12758, 12758, 0, 0, 7),
|
||||
(12758, 12762, 0, 0, 7),
|
||||
(12758, 12761, 0, 0, 7),
|
||||
(12759, 12759, 0, 0, 7),
|
||||
(12759, 12763, 0, 0, 7),
|
||||
(12759, 12764, 0, 0, 7),
|
||||
(12760, 12760, 0, 0, 7),
|
||||
(12760, 12765, 0, 0, 7),
|
||||
(12760, 12766, 0, 0, 7);
|
||||
|
||||
DELETE FROM `linked_respawn` WHERE `linkedGuid` = 127214 AND `linkType` = 0;
|
||||
INSERT INTO `linked_respawn` (`guid`, `linkedGuid`, `linkType`) VALUES
|
||||
|
||||
@@ -2,41 +2,41 @@
|
||||
-- Hellfire Peninsula - Pools of Aggonar ooze aggro linking
|
||||
DELETE FROM `creature_formations` WHERE `leaderGUID` BETWEEN 58465 and 58497;
|
||||
DELETE FROM `creature_formations` WHERE `leaderGUID` = 58786;
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(58465, 58465, 3), (58465, 58498, 3), (58465, 58499, 3),
|
||||
(58466, 58466, 3), (58466, 58500, 3), (58466, 58504, 3),
|
||||
(58467, 58467, 3), (58467, 58501, 3), (58467, 58503, 3),
|
||||
(58468, 58468, 3), (58468, 58561, 3), (58468, 58562, 3),
|
||||
(58469, 58469, 3), (58469, 58507, 3), (58469, 58508, 3),
|
||||
(58470, 58470, 3), (58470, 58509, 3), (58470, 58510, 3),
|
||||
(58471, 58471, 3), (58471, 58502, 3), (58471, 58787, 3),
|
||||
(58786, 58786, 3), (58786, 58513, 3), (58786, 58514, 3),
|
||||
(58472, 58472, 3), (58472, 58511, 3), (58472, 58512, 3),
|
||||
(58473, 58473, 3), (58473, 58515, 3), (58473, 58516, 3),
|
||||
(58474, 58474, 3), (58474, 58517, 3), (58474, 58518, 3),
|
||||
(58475, 58475, 3), (58475, 58519, 3), (58475, 58520, 3),
|
||||
(58476, 58476, 3), (58476, 58521, 3), (58476, 58522, 3),
|
||||
(58477, 58477, 3), (58477, 58523, 3), (58477, 58524, 3),
|
||||
(58478, 58478, 3), (58478, 58525, 3), (58478, 58526, 3),
|
||||
(58479, 58479, 3), (58479, 58527, 3), (58479, 58528, 3),
|
||||
(58480, 58480, 3), (58480, 58529, 3), (58480, 58530, 3),
|
||||
(58481, 58481, 3), (58481, 58531, 3), (58481, 58532, 3),
|
||||
(58482, 58482, 3), (58482, 58535, 3), (58482, 58536, 3),
|
||||
(58483, 58483, 3), (58483, 58533, 3), (58483, 58534, 3),
|
||||
(58484, 58484, 3), (58484, 58537, 3), (58484, 58538, 3),
|
||||
(58485, 58485, 3), (58485, 58539, 3), (58485, 58540, 3),
|
||||
(58486, 58486, 3), (58486, 58541, 3), (58486, 58542, 3),
|
||||
(58487, 58487, 3), (58487, 58543, 3), (58487, 58544, 3),
|
||||
(58488, 58488, 3), (58488, 58545, 3), (58488, 58546, 3),
|
||||
(58489, 58489, 3), (58489, 58552, 3), (58489, 58553, 3),
|
||||
(58490, 58490, 3), (58490, 58554, 3), (58490, 58547, 3),
|
||||
(58491, 58491, 3), (58491, 58549, 3), (58491, 58548, 3),
|
||||
(58492, 58492, 3), (58492, 58550, 3), (58492, 58551, 3),
|
||||
(58493, 58493, 3), (58493, 58555, 3), (58493, 58556, 3),
|
||||
(58494, 58494, 3), (58494, 58557, 3), (58494, 58558, 3),
|
||||
(58495, 58495, 3), (58495, 58559, 3), (58495, 58560, 3),
|
||||
(58496, 58496, 3), (58496, 58505, 3), (58496, 58506, 3),
|
||||
(58497, 58497, 3), (58497, 58563, 3), (58497, 58564, 3);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(58465, 58465, 0, 0, 3), (58465, 58498, 0, 0, 3), (58465, 58499, 0, 0, 3),
|
||||
(58466, 58466, 0, 0, 3), (58466, 58500, 0, 0, 3), (58466, 58504, 0, 0, 3),
|
||||
(58467, 58467, 0, 0, 3), (58467, 58501, 0, 0, 3), (58467, 58503, 0, 0, 3),
|
||||
(58468, 58468, 0, 0, 3), (58468, 58561, 0, 0, 3), (58468, 58562, 0, 0, 3),
|
||||
(58469, 58469, 0, 0, 3), (58469, 58507, 0, 0, 3), (58469, 58508, 0, 0, 3),
|
||||
(58470, 58470, 0, 0, 3), (58470, 58509, 0, 0, 3), (58470, 58510, 0, 0, 3),
|
||||
(58471, 58471, 0, 0, 3), (58471, 58502, 0, 0, 3), (58471, 58787, 0, 0, 3),
|
||||
(58786, 58786, 0, 0, 3), (58786, 58513, 0, 0, 3), (58786, 58514, 0, 0, 3),
|
||||
(58472, 58472, 0, 0, 3), (58472, 58511, 0, 0, 3), (58472, 58512, 0, 0, 3),
|
||||
(58473, 58473, 0, 0, 3), (58473, 58515, 0, 0, 3), (58473, 58516, 0, 0, 3),
|
||||
(58474, 58474, 0, 0, 3), (58474, 58517, 0, 0, 3), (58474, 58518, 0, 0, 3),
|
||||
(58475, 58475, 0, 0, 3), (58475, 58519, 0, 0, 3), (58475, 58520, 0, 0, 3),
|
||||
(58476, 58476, 0, 0, 3), (58476, 58521, 0, 0, 3), (58476, 58522, 0, 0, 3),
|
||||
(58477, 58477, 0, 0, 3), (58477, 58523, 0, 0, 3), (58477, 58524, 0, 0, 3),
|
||||
(58478, 58478, 0, 0, 3), (58478, 58525, 0, 0, 3), (58478, 58526, 0, 0, 3),
|
||||
(58479, 58479, 0, 0, 3), (58479, 58527, 0, 0, 3), (58479, 58528, 0, 0, 3),
|
||||
(58480, 58480, 0, 0, 3), (58480, 58529, 0, 0, 3), (58480, 58530, 0, 0, 3),
|
||||
(58481, 58481, 0, 0, 3), (58481, 58531, 0, 0, 3), (58481, 58532, 0, 0, 3),
|
||||
(58482, 58482, 0, 0, 3), (58482, 58535, 0, 0, 3), (58482, 58536, 0, 0, 3),
|
||||
(58483, 58483, 0, 0, 3), (58483, 58533, 0, 0, 3), (58483, 58534, 0, 0, 3),
|
||||
(58484, 58484, 0, 0, 3), (58484, 58537, 0, 0, 3), (58484, 58538, 0, 0, 3),
|
||||
(58485, 58485, 0, 0, 3), (58485, 58539, 0, 0, 3), (58485, 58540, 0, 0, 3),
|
||||
(58486, 58486, 0, 0, 3), (58486, 58541, 0, 0, 3), (58486, 58542, 0, 0, 3),
|
||||
(58487, 58487, 0, 0, 3), (58487, 58543, 0, 0, 3), (58487, 58544, 0, 0, 3),
|
||||
(58488, 58488, 0, 0, 3), (58488, 58545, 0, 0, 3), (58488, 58546, 0, 0, 3),
|
||||
(58489, 58489, 0, 0, 3), (58489, 58552, 0, 0, 3), (58489, 58553, 0, 0, 3),
|
||||
(58490, 58490, 0, 0, 3), (58490, 58554, 0, 0, 3), (58490, 58547, 0, 0, 3),
|
||||
(58491, 58491, 0, 0, 3), (58491, 58549, 0, 0, 3), (58491, 58548, 0, 0, 3),
|
||||
(58492, 58492, 0, 0, 3), (58492, 58550, 0, 0, 3), (58492, 58551, 0, 0, 3),
|
||||
(58493, 58493, 0, 0, 3), (58493, 58555, 0, 0, 3), (58493, 58556, 0, 0, 3),
|
||||
(58494, 58494, 0, 0, 3), (58494, 58557, 0, 0, 3), (58494, 58558, 0, 0, 3),
|
||||
(58495, 58495, 0, 0, 3), (58495, 58559, 0, 0, 3), (58495, 58560, 0, 0, 3),
|
||||
(58496, 58496, 0, 0, 3), (58496, 58505, 0, 0, 3), (58496, 58506, 0, 0, 3),
|
||||
(58497, 58497, 0, 0, 3), (58497, 58563, 0, 0, 3), (58497, 58564, 0, 0, 3);
|
||||
|
||||
-- Reposition oozes to be closer to group members
|
||||
UPDATE `creature` SET `position_x`=381.658, `position_y`=3482.38, `position_z`=61.609, `orientation`=5.43889 WHERE `guid` = 58496 AND `id1` = 16901;
|
||||
|
||||
@@ -392,27 +392,27 @@ INSERT INTO `creature_addon` (`guid`, `path_id`) VALUES
|
||||
(@GUID+28, (@GUID+28)*10);
|
||||
|
||||
DELETE FROM `creature_formations` WHERE `leaderGUID` IN (@GUID+32,@GUID+38,@GUID+41,@GUID+47,@GUID+16,@GUID+4,@GUID+9,@GUID+12,@GUID+2,@GUID+26);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `groupAI`) VALUES
|
||||
(@GUID+32, @GUID+32, 3),
|
||||
(@GUID+32, @GUID+33, 3),
|
||||
(@GUID+2, @GUID+2 , 3),
|
||||
(@GUID+2, @GUID+3 , 3),
|
||||
(@GUID+26, @GUID+26, 3),
|
||||
(@GUID+26, @GUID+27, 3),
|
||||
(@GUID+38, @GUID+38, 3),
|
||||
(@GUID+38, @GUID+39, 3),
|
||||
(@GUID+41, @GUID+41, 3),
|
||||
(@GUID+41, @GUID+42, 3),
|
||||
(@GUID+47, @GUID+47, 3),
|
||||
(@GUID+47, @GUID+48, 3),
|
||||
(@GUID+16, @GUID+16, 3),
|
||||
(@GUID+16, @GUID+17, 3),
|
||||
(@GUID+4, @GUID+4 , 3),
|
||||
(@GUID+4, @GUID+5 , 3),
|
||||
(@GUID+9, @GUID+9 , 3),
|
||||
(@GUID+9, @GUID+10, 3),
|
||||
(@GUID+12, @GUID+12, 3),
|
||||
(@GUID+12, @GUID+13, 3);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES
|
||||
(@GUID+32, @GUID+32, 0, 0, 3),
|
||||
(@GUID+32, @GUID+33, 0, 0, 3),
|
||||
(@GUID+2, @GUID+2, 0, 0, 3),
|
||||
(@GUID+2, @GUID+3, 0, 0, 3),
|
||||
(@GUID+26, @GUID+26, 0, 0, 3),
|
||||
(@GUID+26, @GUID+27, 0, 0, 3),
|
||||
(@GUID+38, @GUID+38, 0, 0, 3),
|
||||
(@GUID+38, @GUID+39, 0, 0, 3),
|
||||
(@GUID+41, @GUID+41, 0, 0, 3),
|
||||
(@GUID+41, @GUID+42, 0, 0, 3),
|
||||
(@GUID+47, @GUID+47, 0, 0, 3),
|
||||
(@GUID+47, @GUID+48, 0, 0, 3),
|
||||
(@GUID+16, @GUID+16, 0, 0, 3),
|
||||
(@GUID+16, @GUID+17, 0, 0, 3),
|
||||
(@GUID+4, @GUID+4, 0, 0, 3),
|
||||
(@GUID+4, @GUID+5, 0, 0, 3),
|
||||
(@GUID+9, @GUID+9, 0, 0, 3),
|
||||
(@GUID+9, @GUID+10, 0, 0, 3),
|
||||
(@GUID+12, @GUID+12, 0, 0, 3),
|
||||
(@GUID+12, @GUID+13, 0, 0, 3);
|
||||
|
||||
DELETE FROM `creature_addon` WHERE `guid` IN (@GUID+32,@GUID+33,@GUID+2 ,@GUID+3 ,@GUID+26,@GUID+27,@GUID+38,@GUID+39,@GUID+41,@GUID+42,@GUID+47,@GUID+48,@GUID+16,@GUID+17,@GUID+4 ,@GUID+5 ,@GUID+9 ,@GUID+10,@GUID+12,@GUID+13);
|
||||
INSERT INTO `creature_addon` (`guid`, `bytes1`) VALUES
|
||||
|
||||
@@ -30,3 +30,14 @@ Auto-detected by `modules/CMakeLists.txt` (`GetModuleSourceList` globs
|
||||
every subdirectory). No additional CMake plumbing is needed; rebuild
|
||||
the worldserver image and the loader symbol `Addmod_paragonScripts`
|
||||
gets linked into the static `modules` target.
|
||||
|
||||
## SQL layout
|
||||
|
||||
SQL files live under `data/sql/db-world/base/` and
|
||||
`data/sql/db-characters/base/` — the standard AzerothCore module path
|
||||
that the built-in DBUpdater scans (see
|
||||
`src/server/database/Updater/UpdateFetcher.cpp`). Files placed there
|
||||
are applied automatically by `worldserver` / `dbimport` on startup and
|
||||
recorded by hash in the `updates` table of the target database, so
|
||||
re-runs are idempotent. Any new SQL added under those directories will
|
||||
be picked up on the next container/server start without manual import.
|
||||
|
||||
@@ -12,6 +12,11 @@ Paragon.StickyComboPoints = 1
|
||||
# in addition to runes/runic power. Required for the patch-enUS-5.MPQ player
|
||||
# frame to populate Mana/Rage/Energy bars - otherwise the server treats those
|
||||
# powers as inactive and never sends max values, leaving the bars empty.
|
||||
# Also required for core rage generation: Unit::DealDamage only calls
|
||||
# RewardRage() when the attacker HasActivePowerType(POWER_RAGE); if this is off,
|
||||
# Paragon white swings never grant rage (users without this line in any loaded
|
||||
# config used to hit the C++ fallback default of false). Default is on; set 0
|
||||
# only if you intentionally want a stripped-down Paragon test build.
|
||||
Paragon.MultiResource.HasActivePowers = 1
|
||||
|
||||
# Ability / Talent Essence (AE/TE) — Ascension-inspired currency
|
||||
@@ -23,12 +28,27 @@ Paragon.Currency.GrantLevelMin = 10
|
||||
Paragon.Currency.AE.PerLevel = 1
|
||||
Paragon.Currency.TE.PerLevel = 1
|
||||
# Flat TE cost per successful talent rank learn (hook runs once per LearnTalent).
|
||||
# Applies to passive / aura-only talents (addToSpellBook == 0).
|
||||
Paragon.Currency.TE.TalentLearnCost = 1
|
||||
# AE cost per rank for addToSpellBook talents (Starfall, Bladestorm, …).
|
||||
# Those talents also charge TE.TalentLearnCost per rank — each rank costs
|
||||
# both essences at the configured amounts.
|
||||
Paragon.Currency.AE.TalentLearnCost = 1
|
||||
# Default AE cost when spell is not listed in world.paragon_spell_ae_cost.
|
||||
Paragon.Currency.AE.DefaultSpellCost = 2
|
||||
# (Phase 3a: every learnable spell baked into the Character Advancement panel
|
||||
# has an explicit row of value 1 in that table, so this default only kicks in
|
||||
# for spells learned via .paragon learn that aren't in the panel's bake.)
|
||||
Paragon.Currency.AE.DefaultSpellCost = 1
|
||||
|
||||
# Diagnostics ----------------------------------------------------------------
|
||||
# When enabled, dumps every Paragon's rune cooldown state to the server log
|
||||
# every 5 seconds (channel "module"). Use with `.paragon runes` for in-game
|
||||
# verification. Leave at 0 in production — it's noisy.
|
||||
Paragon.Diag.RuneTrace = 0
|
||||
|
||||
# When enabled, traces every PanelLearnSpellChain commit: chain ids, before/
|
||||
# after spell-map sizes for each rank learn, and the classification of every
|
||||
# auto-granted side spell (chain rank / passive dep / active dep revoked).
|
||||
# Use to diagnose "spell X reappears in spellbook on relog" style bugs.
|
||||
# Leave at 0 in production.
|
||||
Paragon.Diag.PanelLearn = 0
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
-- Spells and talents learned only through Character Advancement (Lock In / .paragon learn).
|
||||
-- Apply to the character database (same as `characters`, `character_spell`, etc.).
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_panel_spells` (
|
||||
`guid` INT UNSIGNED NOT NULL COMMENT 'characters.guid',
|
||||
`spell_id` INT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`guid`, `spell_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: spells purchased via Character Advancement';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_panel_talents` (
|
||||
`guid` INT UNSIGNED NOT NULL COMMENT 'characters.guid',
|
||||
`talent_id` SMALLINT UNSIGNED NOT NULL,
|
||||
`rank` TINYINT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`guid`, `talent_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: talent ranks purchased via Character Advancement';
|
||||
|
||||
-- Passive "dependent" spells that AzerothCore's `addSpell` machinery
|
||||
-- (via spell_learn_spell + SPELL_EFFECT_LEARN_SPELL) auto-grants when a
|
||||
-- panel-purchased spell is learned. We keep them learned (some are
|
||||
-- required for the parent ability to function -- e.g. Frost Fever for
|
||||
-- Icy Touch, Blood Plague for Plague Strike) but record them here so
|
||||
-- Reset Abilities / Reset Everything can unlearn them alongside the
|
||||
-- parent. Only passive auto-learns are tracked here; active dependents
|
||||
-- (Death Coil, Death Grip, ...) are revoked at learn time and never
|
||||
-- reach this table.
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_panel_spell_children` (
|
||||
`guid` INT UNSIGNED NOT NULL COMMENT 'characters.guid',
|
||||
`parent_spell_id` INT UNSIGNED NOT NULL COMMENT 'character_paragon_panel_spells.spell_id',
|
||||
`child_spell_id` INT UNSIGNED NOT NULL COMMENT 'auto-learned passive spell id',
|
||||
PRIMARY KEY (`guid`, `parent_spell_id`, `child_spell_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: passive auto-learn dependents to unlearn on reset';
|
||||
|
||||
-- Active "dependent" spells that AzerothCore's `addSpell` /
|
||||
-- `learnSkillRewardedSpells` machinery auto-grants alongside a
|
||||
-- panel-purchased spell but that the player did NOT buy (e.g. Blood
|
||||
-- Presence, Death Coil, Death Grip from purchasing Plague Strike).
|
||||
-- We revoke them at panel-commit time, but AC's skill cascade re-runs
|
||||
-- on every login (`Player::_LoadSkills` -> `learnSkillRewardedSpells`)
|
||||
-- and silently re-grants them. We persist the revoke decisions here
|
||||
-- and re-revoke at `OnPlayerLogin` so the spellbook stays in sync with
|
||||
-- what the player actually purchased through Character Advancement.
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_panel_spell_revoked` (
|
||||
`guid` INT UNSIGNED NOT NULL COMMENT 'characters.guid',
|
||||
`parent_spell_id` INT UNSIGNED NOT NULL COMMENT 'character_paragon_panel_spells.spell_id',
|
||||
`revoked_spell_id` INT UNSIGNED NOT NULL COMMENT 'active spell id auto-granted by skill cascade and revoked',
|
||||
PRIMARY KEY (`guid`, `parent_spell_id`, `revoked_spell_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: active auto-learn dependents to keep revoked across logins';
|
||||
@@ -0,0 +1,20 @@
|
||||
-- mod-paragon: tracking table for active spell dependents the panel
|
||||
-- revoked at commit time. AzerothCore's `Player::_LoadSkills` ->
|
||||
-- `learnSkillRewardedSpells` re-grants skill-rewarded actives (Blood
|
||||
-- Presence, Death Coil, Death Grip, ...) on every login. Persisting
|
||||
-- the revoke decisions here lets `OnPlayerLogin` re-revoke them after
|
||||
-- the cascade has run, so the spellbook stays in sync with what was
|
||||
-- actually purchased through Character Advancement.
|
||||
--
|
||||
-- This file lives under `updates/` so AC's DBUpdater applies it
|
||||
-- incrementally on existing databases (the matching `CREATE TABLE
|
||||
-- IF NOT EXISTS` block in base/character_paragon_panel_learned.sql
|
||||
-- handles fresh installs).
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_panel_spell_revoked` (
|
||||
`guid` INT UNSIGNED NOT NULL COMMENT 'characters.guid',
|
||||
`parent_spell_id` INT UNSIGNED NOT NULL COMMENT 'character_paragon_panel_spells.spell_id',
|
||||
`revoked_spell_id` INT UNSIGNED NOT NULL COMMENT 'active spell id auto-granted by skill cascade and revoked',
|
||||
PRIMARY KEY (`guid`, `parent_spell_id`, `revoked_spell_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: active auto-learn dependents to keep revoked across logins';
|
||||
@@ -0,0 +1,62 @@
|
||||
-- mod-paragon Character Advancement: Build catalog (saved loadouts).
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- A "build" is a named, icon-tagged loadout of panel-purchased spells and
|
||||
-- talent ranks. Each Paragon character can save many builds and swap
|
||||
-- between them via the Builds page in the Character Advancement panel.
|
||||
--
|
||||
-- Swap workflow (see HandleBuildLoad in Paragon_Builds.cpp):
|
||||
-- 1. If a build is currently active, snapshot the player's current
|
||||
-- panel-purchased spells + per-spec talent ranks into that build's
|
||||
-- recipe rows (overwriting the stored recipe).
|
||||
-- 2. If the active build's hunter pet is currently summoned, unsummon
|
||||
-- it to PET_SAVE_NOT_IN_SLOT and store its `pet_number` on the
|
||||
-- active build row so it can be restored on swap-back.
|
||||
-- 3. Reset all panel-bought abilities and talents (refunding AE/TE).
|
||||
-- 4. Re-buy each spell + talent in the target build's recipe (charging
|
||||
-- AE/TE; aborts if insufficient AE/TE -- player keeps refunded
|
||||
-- currency in that case and active becomes NULL).
|
||||
-- 5. Move the target build's parked pet (if any) back to current.
|
||||
-- 6. Update active_build pointer.
|
||||
--
|
||||
-- Pet ownership: a parked pet sits in `character_pet` with slot=100
|
||||
-- (PET_SAVE_NOT_IN_SLOT), exactly like the engine's stable-master
|
||||
-- offload, but tied to the build via `pet_number` instead of any
|
||||
-- in-game stable slot. Build deletion drops the parked pet rows
|
||||
-- entirely (PET_SAVE_AS_DELETED equivalent) -- player is warned.
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_builds` (
|
||||
`build_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`guid` INT UNSIGNED NOT NULL COMMENT 'characters.guid',
|
||||
`name` VARCHAR(32) NOT NULL,
|
||||
`icon` VARCHAR(64) NOT NULL DEFAULT 'INV_Misc_QuestionMark',
|
||||
`is_favorite` TINYINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
`pet_number` INT UNSIGNED NULL COMMENT 'character_pet.id of parked hunter pet, NULL when no pet bound to this build',
|
||||
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`build_id`),
|
||||
KEY `idx_guid` (`guid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: saved Character Advancement build catalog';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_build_spells` (
|
||||
`build_id` INT UNSIGNED NOT NULL,
|
||||
`spell_id` INT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`build_id`, `spell_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: per-build recipe -- panel-purchased spells';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_build_talents` (
|
||||
`build_id` INT UNSIGNED NOT NULL,
|
||||
`spec` TINYINT UNSIGNED NOT NULL COMMENT '0 = primary spec, 1 = secondary (dual spec)',
|
||||
`talent_id` SMALLINT UNSIGNED NOT NULL,
|
||||
`rank` TINYINT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`build_id`, `spec`, `talent_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: per-build recipe -- panel-purchased talent ranks per spec';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_active_build` (
|
||||
`guid` INT UNSIGNED NOT NULL COMMENT 'characters.guid',
|
||||
`build_id` INT UNSIGNED NOT NULL COMMENT 'character_paragon_builds.build_id (per-character active pointer)',
|
||||
PRIMARY KEY (`guid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: pointer to whichever build is currently loaded (one row per Paragon character)';
|
||||
@@ -0,0 +1,30 @@
|
||||
-- mod-paragon Character Advancement: Builds catalog schema cleanup.
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- Two changes:
|
||||
-- 1. Drop `is_favorite` -- the favorite flag and shift-click-to-favorite
|
||||
-- flow are removed. Builds are now ordered solely by build_id ASC.
|
||||
-- 2. Add `share_code` CHAR(6) -- a random alphanumeric token generated
|
||||
-- server-side at build creation that uniquely identifies a saved
|
||||
-- build across the realm. Players exchange codes out-of-band and
|
||||
-- use the BuildsPane "Load Build!" share box to import a copy of
|
||||
-- the build (name + icon + spell + talent recipe) into their own
|
||||
-- catalog. The copy gets a fresh share_code so re-sharing is
|
||||
-- always traceable to the latest owner; the original isn't touched.
|
||||
--
|
||||
-- The column is NULL-tolerant so any rows that pre-date this migration
|
||||
-- (created under 2026_05_10_03's schema) coexist cleanly. The server
|
||||
-- backfills NULLs lazily in PushBuildCatalog -- the next time a player
|
||||
-- opens the BuildsPane on a Paragon character, any of their builds that
|
||||
-- still have a NULL share_code will get one generated and persisted.
|
||||
--
|
||||
-- Charset: 31 unambiguous chars (A-Z minus I/O minus 0/1) gives 31^6 ~=
|
||||
-- 887M codes; collision retry on insert keeps probability of a duplicate
|
||||
-- vanishing for any realistic catalog size.
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
ALTER TABLE `character_paragon_builds`
|
||||
DROP COLUMN `is_favorite`,
|
||||
ADD COLUMN `share_code` CHAR(6) NULL DEFAULT NULL
|
||||
COMMENT 'random alphanumeric token for import-by-code; lazily generated'
|
||||
AFTER `icon`,
|
||||
ADD UNIQUE INDEX `uk_share_code` (`share_code`);
|
||||
@@ -0,0 +1,34 @@
|
||||
-- mod-paragon: preserve superseded share codes as importable snapshots.
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- When an active build is updated (Learn All), the live row gets a new
|
||||
-- share_code and a fresh recipe. Older codes the player posted to Discord
|
||||
-- must keep working: each retired code is frozen here with its spell/talent
|
||||
-- recipe so `C BUILD IMPORT <code>` still materializes that exact loadout.
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_build_share_archive` (
|
||||
`share_code` CHAR(6) NOT NULL COMMENT 'retired code (same charset as live builds)',
|
||||
`name` VARCHAR(32) NOT NULL,
|
||||
`icon` VARCHAR(64) NOT NULL DEFAULT 'INV_Misc_QuestionMark',
|
||||
`archived_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`share_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: frozen build metadata for retired share codes';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_build_share_archive_spells` (
|
||||
`share_code` CHAR(6) NOT NULL,
|
||||
`spell_id` INT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`share_code`, `spell_id`),
|
||||
KEY `idx_share` (`share_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: spell recipe rows for an archived share code';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `character_paragon_build_share_archive_talents` (
|
||||
`share_code` CHAR(6) NOT NULL,
|
||||
`spec` TINYINT UNSIGNED NOT NULL,
|
||||
`talent_id` SMALLINT UNSIGNED NOT NULL,
|
||||
`rank` TINYINT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`share_code`, `spec`, `talent_id`),
|
||||
KEY `idx_share` (`share_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: talent recipe rows for an archived share code';
|
||||
@@ -0,0 +1,446 @@
|
||||
-- Per-spell AE costs for Paragon spell purchases (.paragon learn / panel Lock In).
|
||||
-- Auto-generated by tools/_gen_paragon_spell_ae_cost_sql.py.
|
||||
-- Apply to the *world* database (AzerothCore's SQL updater handles this on worldserver start).
|
||||
-- The flat 1-AE cost is a Phase 3 placeholder; tune individual rows here as the
|
||||
-- economy gets balanced (e.g., 5 AE for top-rank baseline like Cyclone).
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `paragon_spell_ae_cost` (
|
||||
`spell_id` INT UNSIGNED NOT NULL,
|
||||
`ae_cost` SMALLINT UNSIGNED NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`spell_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='mod-paragon: AE cost per spell';
|
||||
|
||||
-- Bulk-load: replace the entire table with the current bake. Manual edits
|
||||
-- to specific rows will be lost when this script regenerates the file --
|
||||
-- track per-spell tuning in a separate INSERT ... ON DUPLICATE KEY UPDATE
|
||||
-- file (e.g. paragon_spell_ae_cost_overrides.sql) if needed.
|
||||
DELETE FROM `paragon_spell_ae_cost`;
|
||||
|
||||
INSERT INTO `paragon_spell_ae_cost` (`spell_id`, `ae_cost`) VALUES
|
||||
(10, 1),
|
||||
(17, 1),
|
||||
(53, 1),
|
||||
(72, 1),
|
||||
(75, 1),
|
||||
(78, 1),
|
||||
(99, 1),
|
||||
(100, 1),
|
||||
(116, 1),
|
||||
(118, 1),
|
||||
(120, 1),
|
||||
(122, 1),
|
||||
(130, 1),
|
||||
(131, 1),
|
||||
(132, 1),
|
||||
(133, 1),
|
||||
(136, 1),
|
||||
(139, 1),
|
||||
(168, 1),
|
||||
(172, 1),
|
||||
(324, 1),
|
||||
(331, 1),
|
||||
(339, 1),
|
||||
(348, 1),
|
||||
(370, 1),
|
||||
(403, 1),
|
||||
(408, 1),
|
||||
(421, 1),
|
||||
(453, 1),
|
||||
(465, 1),
|
||||
(467, 1),
|
||||
(469, 1),
|
||||
(475, 1),
|
||||
(498, 1),
|
||||
(527, 1),
|
||||
(528, 1),
|
||||
(543, 1),
|
||||
(546, 1),
|
||||
(552, 1),
|
||||
(556, 1),
|
||||
(585, 1),
|
||||
(586, 1),
|
||||
(587, 1),
|
||||
(588, 1),
|
||||
(589, 1),
|
||||
(596, 1),
|
||||
(603, 1),
|
||||
(604, 1),
|
||||
(605, 1),
|
||||
(633, 1),
|
||||
(635, 1),
|
||||
(642, 1),
|
||||
(676, 1),
|
||||
(686, 1),
|
||||
(687, 1),
|
||||
(689, 1),
|
||||
(693, 1),
|
||||
(694, 1),
|
||||
(698, 1),
|
||||
(702, 1),
|
||||
(703, 1),
|
||||
(706, 1),
|
||||
(710, 1),
|
||||
(740, 1),
|
||||
(755, 1),
|
||||
(759, 1),
|
||||
(770, 1),
|
||||
(772, 1),
|
||||
(774, 1),
|
||||
(779, 1),
|
||||
(781, 1),
|
||||
(845, 1),
|
||||
(853, 1),
|
||||
(871, 1),
|
||||
(879, 1),
|
||||
(883, 1),
|
||||
(921, 1),
|
||||
(976, 1),
|
||||
(980, 1),
|
||||
(982, 1),
|
||||
(1002, 1),
|
||||
(1008, 1),
|
||||
(1022, 1),
|
||||
(1038, 1),
|
||||
(1044, 1),
|
||||
(1064, 1),
|
||||
(1079, 1),
|
||||
(1082, 1),
|
||||
(1098, 1),
|
||||
(1120, 1),
|
||||
(1126, 1),
|
||||
(1130, 1),
|
||||
(1152, 1),
|
||||
(1160, 1),
|
||||
(1161, 1),
|
||||
(1243, 1),
|
||||
(1449, 1),
|
||||
(1454, 1),
|
||||
(1459, 1),
|
||||
(1462, 1),
|
||||
(1463, 1),
|
||||
(1464, 1),
|
||||
(1490, 1),
|
||||
(1494, 1),
|
||||
(1495, 1),
|
||||
(1499, 1),
|
||||
(1510, 1),
|
||||
(1513, 1),
|
||||
(1515, 1),
|
||||
(1535, 1),
|
||||
(1543, 1),
|
||||
(1680, 1),
|
||||
(1706, 1),
|
||||
(1714, 1),
|
||||
(1715, 1),
|
||||
(1719, 1),
|
||||
(1725, 1),
|
||||
(1752, 1),
|
||||
(1766, 1),
|
||||
(1776, 1),
|
||||
(1784, 1),
|
||||
(1822, 1),
|
||||
(1833, 1),
|
||||
(1842, 1),
|
||||
(1850, 1),
|
||||
(1856, 1),
|
||||
(1943, 1),
|
||||
(1949, 1),
|
||||
(1953, 1),
|
||||
(1966, 1),
|
||||
(1978, 1),
|
||||
(2006, 1),
|
||||
(2008, 1),
|
||||
(2050, 1),
|
||||
(2054, 1),
|
||||
(2060, 1),
|
||||
(2061, 1),
|
||||
(2062, 1),
|
||||
(2094, 1),
|
||||
(2096, 1),
|
||||
(2098, 1),
|
||||
(2120, 1),
|
||||
(2136, 1),
|
||||
(2139, 1),
|
||||
(2362, 1),
|
||||
(2457, 1),
|
||||
(2458, 1),
|
||||
(2484, 1),
|
||||
(2565, 1),
|
||||
(2637, 1),
|
||||
(2641, 1),
|
||||
(2643, 1),
|
||||
(2645, 1),
|
||||
(2687, 1),
|
||||
(2782, 1),
|
||||
(2812, 1),
|
||||
(2825, 1),
|
||||
(2893, 1),
|
||||
(2908, 1),
|
||||
(2912, 1),
|
||||
(2944, 1),
|
||||
(2948, 1),
|
||||
(2973, 1),
|
||||
(2974, 1),
|
||||
(2983, 1),
|
||||
(3034, 1),
|
||||
(3043, 1),
|
||||
(3044, 1),
|
||||
(3045, 1),
|
||||
(3411, 1),
|
||||
(3561, 1),
|
||||
(3562, 1),
|
||||
(3563, 1),
|
||||
(3565, 1),
|
||||
(3566, 1),
|
||||
(3567, 1),
|
||||
(3738, 1),
|
||||
(4987, 1),
|
||||
(5116, 1),
|
||||
(5118, 1),
|
||||
(5138, 1),
|
||||
(5143, 1),
|
||||
(5171, 1),
|
||||
(5176, 1),
|
||||
(5185, 1),
|
||||
(5209, 1),
|
||||
(5211, 1),
|
||||
(5215, 1),
|
||||
(5217, 1),
|
||||
(5221, 1),
|
||||
(5225, 1),
|
||||
(5229, 1),
|
||||
(5246, 1),
|
||||
(5277, 1),
|
||||
(5308, 1),
|
||||
(5384, 1),
|
||||
(5484, 1),
|
||||
(5500, 1),
|
||||
(5502, 1),
|
||||
(5504, 1);
|
||||
|
||||
INSERT INTO `paragon_spell_ae_cost` (`spell_id`, `ae_cost`) VALUES
|
||||
(5675, 1),
|
||||
(5676, 1),
|
||||
(5697, 1),
|
||||
(5730, 1),
|
||||
(5740, 1),
|
||||
(5782, 1),
|
||||
(5938, 1),
|
||||
(6117, 1),
|
||||
(6143, 1),
|
||||
(6196, 1),
|
||||
(6197, 1),
|
||||
(6201, 1),
|
||||
(6229, 1),
|
||||
(6343, 1),
|
||||
(6346, 1),
|
||||
(6353, 1),
|
||||
(6366, 1),
|
||||
(6495, 1),
|
||||
(6552, 1),
|
||||
(6572, 1),
|
||||
(6673, 1),
|
||||
(6770, 1),
|
||||
(6785, 1),
|
||||
(6789, 1),
|
||||
(6940, 1),
|
||||
(7294, 1),
|
||||
(7302, 1),
|
||||
(7384, 1),
|
||||
(8004, 1),
|
||||
(8017, 1),
|
||||
(8024, 1),
|
||||
(8033, 1),
|
||||
(8042, 1),
|
||||
(8050, 1),
|
||||
(8056, 1),
|
||||
(8075, 1),
|
||||
(8092, 1),
|
||||
(8122, 1),
|
||||
(8129, 1),
|
||||
(8143, 1),
|
||||
(8170, 1),
|
||||
(8177, 1),
|
||||
(8181, 1),
|
||||
(8184, 1),
|
||||
(8190, 1),
|
||||
(8227, 1),
|
||||
(8232, 1),
|
||||
(8512, 1),
|
||||
(8647, 1),
|
||||
(8676, 1),
|
||||
(8921, 1),
|
||||
(8936, 1),
|
||||
(8998, 1),
|
||||
(9005, 1),
|
||||
(9484, 1),
|
||||
(10059, 1),
|
||||
(10326, 1),
|
||||
(10595, 1),
|
||||
(11416, 1),
|
||||
(11417, 1),
|
||||
(11418, 1),
|
||||
(11419, 1),
|
||||
(11420, 1),
|
||||
(13159, 1),
|
||||
(13161, 1),
|
||||
(13163, 1),
|
||||
(13165, 1),
|
||||
(13795, 1),
|
||||
(13809, 1),
|
||||
(13813, 1),
|
||||
(14752, 1),
|
||||
(14914, 1),
|
||||
(15237, 1),
|
||||
(16689, 1),
|
||||
(16857, 1),
|
||||
(16914, 1),
|
||||
(18499, 1),
|
||||
(19740, 1),
|
||||
(19742, 1),
|
||||
(19746, 1),
|
||||
(19750, 1),
|
||||
(19752, 1),
|
||||
(19801, 1),
|
||||
(19876, 1),
|
||||
(19878, 1),
|
||||
(19879, 1),
|
||||
(19880, 1),
|
||||
(19882, 1),
|
||||
(19883, 1),
|
||||
(19884, 1),
|
||||
(19885, 1),
|
||||
(19888, 1),
|
||||
(19891, 1),
|
||||
(20043, 1),
|
||||
(20154, 1),
|
||||
(20164, 1),
|
||||
(20165, 1),
|
||||
(20166, 1),
|
||||
(20217, 1),
|
||||
(20230, 1),
|
||||
(20252, 1),
|
||||
(20484, 1),
|
||||
(20736, 1),
|
||||
(21084, 1),
|
||||
(21562, 1),
|
||||
(21849, 1),
|
||||
(22568, 1),
|
||||
(22570, 1),
|
||||
(22812, 1),
|
||||
(22842, 1),
|
||||
(23028, 1),
|
||||
(23920, 1),
|
||||
(23922, 1),
|
||||
(24275, 1),
|
||||
(25780, 1),
|
||||
(25782, 1),
|
||||
(25894, 1),
|
||||
(25898, 1),
|
||||
(25899, 1),
|
||||
(26573, 1),
|
||||
(26679, 1),
|
||||
(27243, 1),
|
||||
(27681, 1),
|
||||
(27683, 1),
|
||||
(28176, 1),
|
||||
(29166, 1),
|
||||
(29722, 1),
|
||||
(29858, 1),
|
||||
(29893, 1),
|
||||
(30451, 1),
|
||||
(30455, 1),
|
||||
(30482, 1),
|
||||
(31224, 1),
|
||||
(31789, 1),
|
||||
(31801, 1),
|
||||
(31884, 1),
|
||||
(32182, 1),
|
||||
(32223, 1),
|
||||
(32266, 1),
|
||||
(32267, 1),
|
||||
(32271, 1),
|
||||
(32272, 1),
|
||||
(32375, 1),
|
||||
(32379, 1),
|
||||
(32546, 1),
|
||||
(32645, 1),
|
||||
(33076, 1),
|
||||
(33690, 1),
|
||||
(33691, 1),
|
||||
(33745, 1),
|
||||
(33763, 1),
|
||||
(33786, 1),
|
||||
(34026, 1),
|
||||
(34074, 1),
|
||||
(34428, 1),
|
||||
(34433, 1),
|
||||
(34477, 1),
|
||||
(34600, 1),
|
||||
(35715, 1),
|
||||
(35717, 1),
|
||||
(36936, 1),
|
||||
(42650, 1),
|
||||
(42955, 1),
|
||||
(43265, 1),
|
||||
(43987, 1),
|
||||
(44614, 1),
|
||||
(45438, 1),
|
||||
(45462, 1),
|
||||
(45477, 1),
|
||||
(45524, 1),
|
||||
(45529, 1),
|
||||
(45902, 1),
|
||||
(46584, 1),
|
||||
(47476, 1),
|
||||
(47528, 1),
|
||||
(47541, 1),
|
||||
(47568, 1),
|
||||
(47897, 1),
|
||||
(48018, 1),
|
||||
(48020, 1),
|
||||
(48045, 1),
|
||||
(48263, 1),
|
||||
(48265, 1),
|
||||
(48266, 1),
|
||||
(48707, 1),
|
||||
(48721, 1),
|
||||
(48743, 1),
|
||||
(48792, 1),
|
||||
(49020, 1),
|
||||
(49358, 1),
|
||||
(49359, 1),
|
||||
(49360, 1),
|
||||
(49361, 1),
|
||||
(49576, 1),
|
||||
(49998, 1),
|
||||
(50464, 1),
|
||||
(50842, 1),
|
||||
(51722, 1),
|
||||
(51723, 1),
|
||||
(52610, 1);
|
||||
|
||||
INSERT INTO `paragon_spell_ae_cost` (`spell_id`, `ae_cost`) VALUES
|
||||
(53140, 1),
|
||||
(53142, 1),
|
||||
(53407, 1),
|
||||
(53408, 1),
|
||||
(53600, 1),
|
||||
(53601, 1),
|
||||
(53736, 1),
|
||||
(54428, 1),
|
||||
(55342, 1),
|
||||
(55694, 1),
|
||||
(56641, 1),
|
||||
(57755, 1),
|
||||
(57934, 1),
|
||||
(57994, 1),
|
||||
(60192, 1),
|
||||
(61846, 1),
|
||||
(62078, 1),
|
||||
(62124, 1),
|
||||
(62757, 1),
|
||||
(64382, 1),
|
||||
(64843, 1);
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
-- mod-paragon: server-side DBC overlay for class 12 (Paragon).
|
||||
-- Auto-generated by fractured-tooling/from-workspace-root/
|
||||
-- _gen_paragon_dbc_overlay_sql.py
|
||||
--
|
||||
-- AzerothCore's DBCStores.cpp::LoadDBC merges every <table>_dbc
|
||||
-- world-DB row on top of the on-disk DBC store at startup
|
||||
-- (storage.LoadFromDB). We use that to ship Paragon's class-12
|
||||
-- DBC deltas in SQL form so a stock data/dbc/ tree (e.g. the
|
||||
-- vanilla `ac-wotlk-client-data` Docker image) still resolves
|
||||
-- class 12 in sChrClassesStore and class-12 entries in
|
||||
-- sSkillRaceClassInfoStore.
|
||||
--
|
||||
-- Without this migration, fresh installs hit:
|
||||
-- CHAR_CREATE_FAILED -- "Class (12) not found in DBC ..."
|
||||
-- the moment a contributor tries to roll a Paragon character.
|
||||
--
|
||||
-- This file is regenerated end-to-end from patch-enUS-4.MPQ;
|
||||
-- do not hand-edit. Update the patched DBC source and rerun
|
||||
-- the bake script.
|
||||
|
||||
-- chrclasses_dbc: classes added or modified by patch-enUS-4.MPQ.
|
||||
-- AzerothCore merges this on top of the on-disk ChrClasses.dbc
|
||||
-- so a stock data/dbc tree still gets class 12 at runtime.
|
||||
DELETE FROM `chrclasses_dbc` WHERE `ID` IN (12);
|
||||
INSERT INTO `chrclasses_dbc` (`ID`,`Field01`,`DisplayPower`,`PetNameToken`,`Name_Lang_enUS`,`Name_Lang_Mask`,`Name_Female_Lang_Mask`,`Name_Male_Lang_Mask`,`Filename`,`SpellClassSet`,`Flags`,`CinematicSequenceID`,`Required_Expansion`) VALUES
|
||||
(12, 0, 0, 0, 'Paragon', 0, 0, 0, 'PARAGON', 4, 50, 0, 2);
|
||||
|
||||
-- skillraceclassinfo_dbc: rows where patch-enUS-4 OR'd the
|
||||
-- class-12 bit (0x800) into ClassMask, opening every
|
||||
-- baseline skill to Paragon. Replaces the stock row by ID so
|
||||
-- AzerothCore picks the patched mask on the SQL merge pass.
|
||||
DELETE FROM `skillraceclassinfo_dbc` WHERE `ID` IN (
|
||||
57,301,107,82,75,140,328,638,872,880,881,885,886,910,117,335,628,629,630,912,126,127,133,134,635,31,39,135,325,636,637,643,644,888,889,914,125,626,884,898,901,58,60,916,59,40,41,68,48,49,44,45,42,43,50,51,131,132,883,913,105,71,70,69,925,54,25,138,139,91,882,85,84,93,88,865,87,441,94,443,92,481,89,442,123,124,624,625,702,908,6,922,33,243,899,241,122,621,622,701,907,970,129,323,631,632,633,634,641,642,142,143,639,640,28,63,282,29,284,65,97,244,940,72,128,878,879,137,144,136,915,55,79,81,76,149,112,111,106,66,26,83,74,73,108,109,110,113,38,35,36,37,61,62,64,24,34,21,906,46,47,52,53,281,104,102,101,27,95,98,96,30,145,146,147,148,151,155,158,159,271,175,178,183,186,270,189,191,193,198,200,265,266,203,204,205,268,269,246,272,330,381,403,445,446,461,501,463,464,521,522,541,544,581,601,741,742,781,841,861,862,866,867,877,934,892,896,897,951,895,900,936,938,939,947
|
||||
);
|
||||
INSERT INTO `skillraceclassinfo_dbc` (`ID`,`SkillID`,`RaceMask`,`ClassMask`,`Flags`,`MinLevel`,`SkillTierID`,`SkillCostIndex`) VALUES
|
||||
(57,6,-1,2176,1040,0,0,0),
|
||||
(301,8,-1,2176,1040,0,0,0),
|
||||
(107,26,-1,2049,1040,0,0,0),
|
||||
(82,38,-1,2056,1040,0,0,0),
|
||||
(75,39,-1,2056,1040,0,0,0),
|
||||
(140,43,1115,2049,128,0,0,0),
|
||||
(328,43,3071,2052,128,0,0,0),
|
||||
(638,43,164,2049,128,0,0,0),
|
||||
(872,43,32767,2056,128,0,0,0),
|
||||
(880,43,1024,2052,128,0,0,0),
|
||||
(881,43,32767,2432,128,0,0,0),
|
||||
(885,43,1029,2050,128,0,0,0),
|
||||
(886,43,512,2050,128,0,0,0),
|
||||
(910,43,262143,2080,128,0,0,0),
|
||||
(117,44,166,2052,128,0,0,0),
|
||||
(335,44,2147483647,2122,128,0,0,0),
|
||||
(628,44,1544,2052,128,0,0,0),
|
||||
(629,44,167,2049,128,0,0,0),
|
||||
(630,44,1112,2049,128,0,0,0),
|
||||
(912,44,262143,2080,128,0,0,0),
|
||||
(126,45,650,2052,128,0,0,0),
|
||||
(127,45,32767,2061,128,0,0,0),
|
||||
(133,46,36,2052,128,0,0,0),
|
||||
(134,46,32767,2057,128,0,0,0),
|
||||
(635,46,1674,2052,128,0,0,0),
|
||||
(31,50,-1,2052,1040,0,0,0),
|
||||
(39,51,-1,2052,1040,0,0,0),
|
||||
(135,54,2147483647,2128,128,0,0,0),
|
||||
(325,54,-1,2056,128,0,0,0),
|
||||
(636,54,1133,2049,128,0,0,0),
|
||||
(637,54,658,2049,128,0,0,0),
|
||||
(643,54,8,3072,128,0,0,0),
|
||||
(644,54,32,3072,128,0,0,0),
|
||||
(888,54,261631,2050,128,0,0,0),
|
||||
(889,54,512,2050,128,0,0,0),
|
||||
(914,54,262143,2080,128,0,0,0),
|
||||
(125,55,262143,2052,128,0,0,0),
|
||||
(626,55,163839,2049,128,0,0,0),
|
||||
(884,55,512,2050,128,0,0,0),
|
||||
(898,55,262143,2080,128,0,0,0),
|
||||
(901,55,261631,2050,128,0,0,0),
|
||||
(58,56,-1,2064,1040,0,0,0),
|
||||
(60,78,-1,2064,1040,0,0,0),
|
||||
(916,95,524287,2080,640,0,0,0),
|
||||
(59,96,2047,3072,1168,0,0,0),
|
||||
(40,98,1101,3583,128,0,0,0),
|
||||
(41,98,674,3551,160,0,21,0),
|
||||
(68,101,4,3583,1170,0,0,0),
|
||||
(48,109,690,3583,128,0,0,0),
|
||||
(49,109,1101,3551,160,0,21,0),
|
||||
(44,111,4,3583,128,0,0,0),
|
||||
(45,111,2043,3551,160,0,21,0),
|
||||
(42,113,8,3583,128,0,0,0),
|
||||
(43,113,2039,3551,160,0,21,0),
|
||||
(50,115,32,3583,128,0,0,0),
|
||||
(51,115,2015,3551,160,0,21,0),
|
||||
(131,118,32767,2056,146,1,0,0),
|
||||
(132,118,32767,2053,146,20,0,0),
|
||||
(883,118,32767,2112,402,0,0,0),
|
||||
(913,118,262143,2080,146,0,0,0),
|
||||
(105,120,2047,2304,1170,0,0,0),
|
||||
(71,124,32,3583,1170,0,0,0),
|
||||
(70,125,2,3583,146,0,0,0),
|
||||
(69,126,8,3583,1170,0,0,0),
|
||||
(925,129,-1,2080,128,0,63,0),
|
||||
(54,130,2047,2176,1168,4,0,0),
|
||||
(25,134,-1,3072,1040,10,0,0),
|
||||
(138,136,32767,3536,128,0,0,0),
|
||||
(139,136,32767,2053,128,0,0,0),
|
||||
(91,137,1535,3551,160,0,21,0),
|
||||
(882,137,512,3583,128,0,0,0),
|
||||
(85,138,2047,3583,128,0,0,0),
|
||||
(84,139,2047,3583,160,0,21,0),
|
||||
(93,140,2047,3583,128,0,0,0),
|
||||
(88,141,2047,3583,160,0,21,0),
|
||||
(865,142,2047,3583,0,0,0,0),
|
||||
(87,148,1,3551,1170,0,181,0),
|
||||
(441,148,222,3583,1170,0,182,0),
|
||||
(94,149,2,3551,1170,0,181,0),
|
||||
(443,149,509,3583,1170,0,182,0),
|
||||
(92,150,8,3551,1170,0,181,0),
|
||||
(481,150,215,3583,1170,0,182,0),
|
||||
(89,152,4,3551,1170,0,181,0),
|
||||
(442,152,219,3583,1170,0,182,0),
|
||||
(123,160,262143,2050,128,0,0,0),
|
||||
(124,160,-1,3072,128,0,0,0),
|
||||
(624,160,32,2049,128,0,0,0),
|
||||
(625,160,262111,2049,128,0,0,0),
|
||||
(702,160,-1,2112,128,0,0,0),
|
||||
(908,160,262143,2080,128,0,0,0),
|
||||
(6,162,2147483647,3551,128,0,0,0),
|
||||
(922,162,262143,2080,128,0,0,0),
|
||||
(33,163,-1,2052,1040,0,0,0),
|
||||
(243,164,2047,3583,160,0,41,0),
|
||||
(899,165,2047,3583,160,0,41,0),
|
||||
(241,171,2047,3583,160,0,41,0),
|
||||
(122,172,163839,2050,128,0,0,0),
|
||||
(621,172,6,2049,128,0,0,0),
|
||||
(622,172,1529,2049,128,0,0,0),
|
||||
(701,172,163839,2112,128,0,0,0),
|
||||
(907,172,524287,2080,128,0,0,0),
|
||||
(970,172,163839,2052,128,0,0,0),
|
||||
(129,173,32767,2312,128,0,0,0),
|
||||
(323,173,32767,2256,128,0,0,0),
|
||||
(631,173,520,2052,128,0,0,0),
|
||||
(632,173,1190,2052,128,0,0,0),
|
||||
(633,173,216,2049,128,0,0,0),
|
||||
(634,173,1063,2049,128,0,0,0),
|
||||
(641,173,32,3072,128,0,0,0),
|
||||
(642,173,8,3072,128,0,0,0),
|
||||
(142,176,-1,2056,128,0,0,0),
|
||||
(143,176,-1,2052,128,0,0,0),
|
||||
(639,176,128,2049,128,0,0,0),
|
||||
(640,176,262015,2049,128,0,0,0),
|
||||
(28,182,2047,3583,160,0,2,0),
|
||||
(63,184,-1,2050,1040,0,0,0),
|
||||
(282,185,2047,3583,128,0,61,0),
|
||||
(29,186,2047,3583,160,0,2,0),
|
||||
(284,197,2047,3583,160,0,62,0),
|
||||
(65,198,2047,2050,1168,0,0,0),
|
||||
(97,199,2047,2112,1168,0,0,0),
|
||||
(244,202,2047,3583,160,0,41,0),
|
||||
(940,205,524287,2176,2048,0,0,0),
|
||||
(72,220,16,3583,1170,0,0,0),
|
||||
(128,226,32767,2057,128,0,0,0),
|
||||
(878,226,1024,2052,128,0,0,0),
|
||||
(879,226,31743,2052,128,0,0,0),
|
||||
(137,227,2047,3077,128,0,0,0),
|
||||
(144,228,-1,2448,128,0,0,0),
|
||||
(136,229,32767,3079,128,20,0,0),
|
||||
(915,229,262143,2080,128,0,0,0),
|
||||
(55,237,-1,2176,1040,0,0,0),
|
||||
(79,238,2047,2056,1168,4,0,0),
|
||||
(81,239,2047,2056,1168,0,0,0),
|
||||
(76,241,2047,2056,128,40,0,0),
|
||||
(149,242,2047,2056,1168,16,0,0),
|
||||
(112,243,2047,2049,1170,0,0,0),
|
||||
(111,244,2047,2049,1168,0,0,0),
|
||||
(106,245,2047,2049,1168,0,0,0),
|
||||
(66,246,2047,2050,1168,0,0,0),
|
||||
(26,247,2047,3072,1168,20,0,0),
|
||||
(83,252,2047,2057,128,0,0,0),
|
||||
(74,253,-1,2056,1040,0,0,0),
|
||||
(73,254,2047,2056,1168,10,0,0),
|
||||
(108,255,2047,2049,1168,0,0,0),
|
||||
(109,256,-1,2049,1040,0,0,0),
|
||||
(110,257,-1,2049,1040,0,0,0),
|
||||
(113,258,2047,2049,1168,10,0,0),
|
||||
(38,260,2047,2052,128,0,0,0),
|
||||
(35,262,2047,2052,128,0,0,0),
|
||||
(36,263,2047,2052,128,0,0,0),
|
||||
(37,264,2047,2052,128,0,0,0),
|
||||
(61,267,-1,2050,1040,0,0,0),
|
||||
(62,268,2047,2050,1170,0,0,0),
|
||||
(64,269,2047,2050,1168,0,0,0),
|
||||
(24,272,2047,3072,1168,10,0,0),
|
||||
(34,273,2047,2052,128,0,0,0),
|
||||
(21,293,2047,2051,128,40,0,0),
|
||||
(906,293,262143,2080,128,0,0,0),
|
||||
(46,313,64,3583,128,0,0,0),
|
||||
(47,313,1983,3551,160,0,21,0),
|
||||
(52,315,128,3583,128,0,0,0),
|
||||
(53,315,1919,3551,160,0,21,0),
|
||||
(281,333,2047,3583,160,0,62,0),
|
||||
(104,353,2047,2304,1170,0,0,0),
|
||||
(102,354,-1,2304,1040,0,0,0),
|
||||
(101,355,-1,2304,1040,0,0,0),
|
||||
(27,356,2047,3583,128,0,23,0),
|
||||
(95,373,-1,2112,1040,0,0,0),
|
||||
(98,374,262143,2112,1040,0,0,0),
|
||||
(96,375,262143,2112,1040,0,0,0),
|
||||
(30,393,2047,3583,160,0,161,0),
|
||||
(145,413,2047,2116,128,40,0,0),
|
||||
(146,413,2047,2083,128,0,0,0),
|
||||
(147,414,2047,3183,128,0,0,0),
|
||||
(148,415,2047,3583,128,0,0,0),
|
||||
(151,416,2047,2049,192,0,0,0),
|
||||
(155,416,2047,2050,192,0,0,1),
|
||||
(158,416,2047,3136,192,0,0,1),
|
||||
(159,416,2047,2060,192,0,0,1),
|
||||
(271,416,2047,2448,192,0,0,2),
|
||||
(175,418,2047,2049,384,0,0,0),
|
||||
(178,418,2047,2050,384,0,0,0),
|
||||
(183,418,2047,3332,384,0,0,1),
|
||||
(186,418,2047,2192,384,0,0,1),
|
||||
(270,418,2047,2120,384,0,0,1),
|
||||
(189,419,2047,2060,640,0,0,2),
|
||||
(191,419,2047,3072,640,0,0,1),
|
||||
(193,419,2047,2192,640,0,0,0),
|
||||
(198,419,2047,2050,640,0,0,1),
|
||||
(200,419,2047,2049,640,0,0,2),
|
||||
(265,419,2047,2304,640,0,0,0),
|
||||
(266,419,2047,2112,640,0,0,1),
|
||||
(203,420,2047,2061,1152,0,0,2),
|
||||
(204,420,2047,3074,1152,0,0,1),
|
||||
(205,420,2047,2320,1152,0,0,0),
|
||||
(268,420,2047,2176,1152,0,0,0),
|
||||
(269,420,2047,2112,1152,0,0,1),
|
||||
(246,433,2047,2115,128,0,0,0),
|
||||
(272,453,2047,2051,128,0,0,0),
|
||||
(330,473,4095,3149,130,0,0,0),
|
||||
(381,493,8,3583,164,0,0,0),
|
||||
(403,515,2047,3551,128,0,0,0),
|
||||
(445,533,128,3583,1170,0,181,0),
|
||||
(446,533,95,3551,1170,0,182,0),
|
||||
(461,553,64,3551,1170,0,181,0),
|
||||
(501,553,4,3583,1170,0,182,0),
|
||||
(463,554,16,3551,1170,0,181,0),
|
||||
(464,554,207,3583,1170,0,182,0),
|
||||
(521,573,-1,3072,1040,0,0,0),
|
||||
(522,574,-1,3072,1040,0,0,0),
|
||||
(541,593,-1,2304,1040,0,0,0),
|
||||
(544,594,-1,2050,1040,0,0,0),
|
||||
(581,613,-1,2064,1040,0,0,0),
|
||||
(601,633,-1,2056,128,0,0,0),
|
||||
(741,673,16,3583,128,0,0,0),
|
||||
(742,673,2031,3551,160,0,21,0),
|
||||
(781,713,255,3583,1170,0,181,0),
|
||||
(841,733,128,3583,1170,0,0,0),
|
||||
(861,753,64,3583,1170,0,0,0),
|
||||
(862,754,1,3583,1170,0,0,0),
|
||||
(866,755,2047,3583,160,0,41,0),
|
||||
(867,756,512,3583,146,0,0,0),
|
||||
(877,760,1024,3583,146,0,0,0),
|
||||
(934,762,524287,2080,144,0,223,0),
|
||||
(892,769,32767,3583,1040,0,0,0),
|
||||
(896,770,-1,2080,1040,0,0,0),
|
||||
(897,771,262143,2080,1040,0,0,0),
|
||||
(951,771,2097151,3583,0,0,0,0),
|
||||
(895,772,-1,2080,1040,0,0,0),
|
||||
(900,773,262143,3583,160,0,41,0),
|
||||
(936,776,262143,2080,128,0,0,0),
|
||||
(938,777,524287,3583,2,0,0,0),
|
||||
(939,778,524287,3583,2,0,0,0),
|
||||
(947,778,2097151,3583,0,0,0,0);
|
||||
@@ -0,0 +1,179 @@
|
||||
-- mod-paragon: starter spawn data for class 12 (Paragon).
|
||||
--
|
||||
-- Companion to 2026_05_09_00.sql. The DBC overlay teaches the world
|
||||
-- server that class 12 exists; this migration teaches it WHERE
|
||||
-- characters of that class spawn, what action bar they boot with,
|
||||
-- and what per-level base stats to integrity-check against.
|
||||
--
|
||||
-- Without these rows, character creation fails inside Player::Create:
|
||||
--
|
||||
-- PlayerInfo const* info = sObjectMgr->GetPlayerInfo(race, class);
|
||||
-- if (!info) {
|
||||
-- LOG_ERROR("entities.player",
|
||||
-- "Player::Create: ... invalid race/class pair ({}/{})"
|
||||
-- " - refusing to do so.", ..., race, class);
|
||||
-- return false; // -> client sees "Error creating character"
|
||||
-- }
|
||||
--
|
||||
-- and on world load the player_class_stats integrity check trips:
|
||||
--
|
||||
-- "Class N Level L does not have stats data!"
|
||||
--
|
||||
-- Tables touched:
|
||||
-- - playercreateinfo : (race, class=12) -> map/zone/x/y/z
|
||||
-- Race-specific starting zones (Paragon
|
||||
-- spawns in each race's standard newbie
|
||||
-- area, NOT Acherus, since it is a
|
||||
-- from-level-1 class).
|
||||
-- - playercreateinfo_action : (race, class=12, button) -> action,type
|
||||
-- Default action bar layout per race.
|
||||
-- - player_class_stats : (class=12, level 1..80) -> base stats
|
||||
-- Per-level HP/Mana/STR/AGI/STA/INT/SPI
|
||||
-- used by Player::InitStatsForLevel.
|
||||
--
|
||||
-- Tables intentionally NOT touched here:
|
||||
-- - playercreateinfo_item : Paragon ships no per-class starting
|
||||
-- items; gear comes from the racial
|
||||
-- kit only.
|
||||
-- - playercreateinfo_skills / _cast_spell / _spell_custom :
|
||||
-- These are mask-based. Class-12 baseline
|
||||
-- weapon/defense skills come through
|
||||
-- classMask=0 ("all classes") rows that
|
||||
-- already cover Paragon. The DBC overlay
|
||||
-- in 2026_05_09_00.sql opens
|
||||
-- SkillRaceClassInfo for class 12.
|
||||
|
||||
-- Idempotent: blow away any pre-existing class-12 rows first so this
|
||||
-- migration can be replayed cleanly on a partially-seeded DB (e.g.
|
||||
-- after a contributor manually patched their local DB before this
|
||||
-- migration landed).
|
||||
DELETE FROM `playercreateinfo` WHERE `class` = 12;
|
||||
DELETE FROM `playercreateinfo_action` WHERE `class` = 12;
|
||||
DELETE FROM `player_class_stats` WHERE `Class` = 12;
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- playercreateinfo (10 rows: every DK-eligible race, racial start)
|
||||
-- ---------------------------------------------------------------
|
||||
INSERT INTO `playercreateinfo` (`race`, `class`, `map`, `zone`, `position_x`, `position_y`, `position_z`, `orientation`) VALUES
|
||||
( 1, 12, 0, 12, -8949.95, -132.493, 83.5312, 0 ), -- Human -> Northshire, Elwynn Forest
|
||||
( 2, 12, 1, 14, -618.518, -4251.67, 38.718, 0 ), -- Orc -> Valley of Trials, Durotar
|
||||
( 3, 12, 0, 1, -6240.32, 331.033, 382.758, 6.17716 ), -- Dwarf -> Coldridge Valley, Dun Morogh
|
||||
( 4, 12, 1, 141, 10311.3, 832.463, 1326.41, 5.69632 ), -- Night Elf -> Shadowglen, Teldrassil
|
||||
( 5, 12, 0, 85, 1676.71, 1678.31, 121.67, 2.70526 ), -- Undead -> Deathknell, Tirisfal
|
||||
( 6, 12, 1, 215, -2917.58, -257.98, 52.9968, 0 ), -- Tauren -> Camp Narache, Mulgore
|
||||
( 7, 12, 0, 1, -6240.32, 331.033, 382.758, 0 ), -- Gnome -> Coldridge Valley (shared)
|
||||
( 8, 12, 1, 14, -618.518, -4251.67, 38.718, 0 ), -- Troll -> Valley of Trials (shared)
|
||||
(10, 12, 530, 3431, 10349.6, -6357.29, 33.4026, 5.31605 ), -- Blood Elf -> Sunstrider Isle, Eversong
|
||||
(11, 12, 530, 3526, -3961.64,-13931.2, 100.615, 2.08364 ); -- Draenei -> Ammen Vale, Azuremyst Isle
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- playercreateinfo_action (46 rows)
|
||||
-- Buttons: 72=Attack(6603), 73=Eat(78), 74=racial, 75=race-extra,
|
||||
-- 82=Skinning(59752, Tauren only), 84=Attack, 96=Attack
|
||||
-- ---------------------------------------------------------------
|
||||
INSERT INTO `playercreateinfo_action` (`race`, `class`, `button`, `action`, `type`) VALUES
|
||||
( 1, 12, 72, 6603, 0), ( 1, 12, 73, 78, 0), ( 1, 12, 82, 59752, 0),
|
||||
( 1, 12, 84, 6603, 0), ( 1, 12, 96, 6603, 0),
|
||||
( 2, 12, 72, 6603, 0), ( 2, 12, 73, 78, 0), ( 2, 12, 74, 20572, 0),
|
||||
( 2, 12, 84, 6603, 0), ( 2, 12, 96, 6603, 0),
|
||||
( 3, 12, 72, 6603, 0), ( 3, 12, 73, 78, 0), ( 3, 12, 74, 20594, 0),
|
||||
( 3, 12, 75, 2481, 0), ( 3, 12, 84, 6603, 0), ( 3, 12, 96, 6603, 0),
|
||||
( 4, 12, 72, 6603, 0), ( 4, 12, 73, 78, 0), ( 4, 12, 74, 58984, 0),
|
||||
( 4, 12, 84, 6603, 0), ( 4, 12, 96, 6603, 0),
|
||||
( 5, 12, 72, 6603, 0), ( 5, 12, 73, 78, 0), ( 5, 12, 74, 20577, 0),
|
||||
( 5, 12, 84, 6603, 0), ( 5, 12, 96, 6603, 0),
|
||||
( 6, 12, 72, 6603, 0), ( 6, 12, 73, 78, 0), ( 6, 12, 74, 20549, 0),
|
||||
( 6, 12, 84, 6603, 0), ( 6, 12, 96, 6603, 0),
|
||||
( 7, 12, 72, 6603, 0), ( 7, 12, 73, 78, 0), ( 7, 12, 84, 6603, 0),
|
||||
( 7, 12, 96, 6603, 0),
|
||||
( 8, 12, 72, 6603, 0), ( 8, 12, 73, 78, 0), ( 8, 12, 74, 2764, 0),
|
||||
( 8, 12, 75, 26297, 0), ( 8, 12, 84, 6603, 0), ( 8, 12, 96, 6603, 0),
|
||||
(11, 12, 72, 6603, 0), (11, 12, 73, 78, 0), (11, 12, 74, 28880, 0),
|
||||
(11, 12, 84, 6603, 0), (11, 12, 96, 6603, 0);
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- player_class_stats (80 rows: levels 1..80 per-class base stats)
|
||||
-- Curve mirrors Warrior baseline -> Paladin past 60 (vehicle-style HP
|
||||
-- inflation past 60 to keep Paragon competitive in Wrath content).
|
||||
-- ---------------------------------------------------------------
|
||||
INSERT INTO `player_class_stats` (`Class`, `Level`, `BaseHP`, `BaseMana`, `Strength`, `Agility`, `Stamina`, `Intellect`, `Spirit`) VALUES
|
||||
(12, 1, 20, 60, 23, 20, 22, 20, 20),
|
||||
(12, 2, 29, 66, 24, 21, 23, 20, 20),
|
||||
(12, 3, 38, 73, 25, 21, 24, 20, 21),
|
||||
(12, 4, 47, 81, 26, 22, 25, 20, 21),
|
||||
(12, 5, 56, 90, 28, 23, 26, 20, 21),
|
||||
(12, 6, 65, 100, 29, 24, 27, 21, 21),
|
||||
(12, 7, 74, 111, 30, 24, 28, 21, 22),
|
||||
(12, 8, 83, 123, 31, 25, 29, 21, 22),
|
||||
(12, 9, 92, 136, 32, 26, 30, 21, 22),
|
||||
(12, 10, 97, 150, 33, 26, 31, 21, 23),
|
||||
(12, 11, 103, 165, 35, 27, 33, 21, 23),
|
||||
(12, 12, 109, 182, 36, 28, 34, 21, 23),
|
||||
(12, 13, 118, 200, 37, 29, 35, 21, 24),
|
||||
(12, 14, 128, 219, 39, 30, 36, 22, 24),
|
||||
(12, 15, 139, 239, 40, 30, 37, 22, 24),
|
||||
(12, 16, 151, 260, 41, 31, 38, 22, 25),
|
||||
(12, 17, 154, 282, 42, 32, 40, 22, 25),
|
||||
(12, 18, 168, 305, 44, 33, 41, 22, 25),
|
||||
(12, 19, 183, 329, 45, 34, 42, 22, 26),
|
||||
(12, 20, 199, 354, 47, 35, 43, 22, 26),
|
||||
(12, 21, 206, 380, 48, 35, 45, 23, 26),
|
||||
(12, 22, 224, 392, 49, 36, 46, 23, 27),
|
||||
(12, 23, 243, 420, 51, 37, 47, 23, 27),
|
||||
(12, 24, 253, 449, 52, 38, 49, 23, 28),
|
||||
(12, 25, 274, 479, 54, 39, 50, 23, 28),
|
||||
(12, 26, 296, 509, 55, 40, 51, 23, 28),
|
||||
(12, 27, 309, 524, 57, 41, 53, 23, 29),
|
||||
(12, 28, 333, 554, 58, 42, 54, 24, 29),
|
||||
(12, 29, 348, 584, 60, 43, 56, 24, 30),
|
||||
(12, 30, 374, 614, 62, 44, 57, 24, 30),
|
||||
(12, 31, 401, 629, 63, 45, 58, 24, 30),
|
||||
(12, 32, 419, 659, 65, 46, 60, 24, 31),
|
||||
(12, 33, 448, 689, 66, 47, 61, 24, 31),
|
||||
(12, 34, 468, 704, 68, 48, 63, 25, 32),
|
||||
(12, 35, 499, 734, 70, 49, 64, 25, 32),
|
||||
(12, 36, 521, 749, 72, 50, 66, 25, 33),
|
||||
(12, 37, 545, 779, 73, 51, 68, 25, 33),
|
||||
(12, 38, 581, 809, 75, 52, 69, 25, 33),
|
||||
(12, 39, 609, 824, 77, 53, 71, 26, 34),
|
||||
(12, 40, 649, 854, 79, 54, 72, 26, 34),
|
||||
(12, 41, 681, 869, 80, 56, 74, 26, 35),
|
||||
(12, 42, 715, 899, 82, 57, 76, 26, 35),
|
||||
(12, 43, 761, 914, 84, 58, 77, 26, 36),
|
||||
(12, 44, 799, 944, 86, 59, 79, 26, 36),
|
||||
(12, 45, 839, 959, 88, 60, 81, 27, 37),
|
||||
(12, 46, 881, 989, 90, 61, 83, 27, 37),
|
||||
(12, 47, 935, 1004, 92, 63, 84, 27, 38),
|
||||
(12, 48, 981, 1019, 94, 64, 86, 27, 38),
|
||||
(12, 49, 1029, 1049, 96, 65, 88, 28, 39),
|
||||
(12, 50, 1079, 1064, 98, 66, 90, 28, 39),
|
||||
(12, 51, 1131, 1079, 100, 68, 92, 28, 40),
|
||||
(12, 52, 1185, 1109, 102, 69, 94, 28, 40),
|
||||
(12, 53, 1241, 1124, 104, 70, 96, 28, 41),
|
||||
(12, 54, 1299, 1139, 106, 72, 98, 29, 42),
|
||||
(12, 55, 1359, 1154, 109, 73, 100, 29, 42),
|
||||
(12, 56, 1421, 1169, 111, 74, 102, 29, 43),
|
||||
(12, 57, 1485, 1199, 113, 76, 104, 29, 43),
|
||||
(12, 58, 1551, 1214, 115, 77, 106, 30, 44),
|
||||
(12, 59, 1619, 1229, 118, 79, 108, 30, 44),
|
||||
(12, 60, 1689, 1244, 120, 80, 110, 30, 45),
|
||||
(12, 61, 1902, 1357, 122, 81, 112, 30, 46),
|
||||
(12, 62, 2129, 1469, 125, 83, 114, 30, 46),
|
||||
(12, 63, 2357, 1582, 127, 84, 117, 31, 47),
|
||||
(12, 64, 2612, 1694, 130, 86, 119, 31, 47),
|
||||
(12, 65, 2883, 1807, 132, 88, 121, 31, 48),
|
||||
(12, 66, 3169, 1919, 135, 89, 123, 32, 49),
|
||||
(12, 67, 3455, 2032, 137, 91, 126, 32, 49),
|
||||
(12, 68, 3774, 2145, 140, 92, 128, 32, 50),
|
||||
(12, 69, 4109, 2257, 142, 94, 130, 32, 51),
|
||||
(12, 70, 4444, 2370, 145, 96, 133, 33, 51),
|
||||
(12, 71, 4720, 2482, 148, 97, 135, 33, 52),
|
||||
(12, 72, 5013, 2595, 150, 99, 138, 33, 53),
|
||||
(12, 73, 5325, 2708, 153, 101, 140, 33, 54),
|
||||
(12, 74, 5656, 2820, 156, 102, 143, 34, 54),
|
||||
(12, 75, 6008, 2933, 159, 104, 145, 34, 55),
|
||||
(12, 76, 6381, 3045, 162, 106, 148, 34, 56),
|
||||
(12, 77, 6778, 3158, 165, 108, 151, 35, 57),
|
||||
(12, 78, 7198, 3270, 168, 109, 153, 35, 57),
|
||||
(12, 79, 7646, 3383, 171, 111, 156, 35, 58),
|
||||
(12, 80, 8121, 3496, 174, 113, 159, 36, 59);
|
||||
@@ -0,0 +1,50 @@
|
||||
-- mod-paragon: starter weapon / armor skills for class 12 (Paragon).
|
||||
--
|
||||
-- Companion to 2026_05_10_00.sql. The spawn-data migration teaches
|
||||
-- Player::Create *that* class 12 exists at a given race; this one
|
||||
-- teaches it which weapon and armor skill lines to grant on first
|
||||
-- character login.
|
||||
--
|
||||
-- Without these rows a fresh Paragon character lands in their newbie
|
||||
-- zone with **no** weapon or armor proficiencies (auto-attack greys
|
||||
-- out the moment they equip anything beyond a fist). The classMask=0
|
||||
-- "all classes" rows in playercreateinfo_skills only cover Defense,
|
||||
-- Unarmed, Cloth, the racial / language skills, Mounts and
|
||||
-- Companion Pets -- which is exactly what bare-fisted, naked
|
||||
-- characters look like.
|
||||
--
|
||||
-- Paragon plays every class, so it grants every weapon / armor
|
||||
-- proficiency at level 1. The skillline rows themselves are still
|
||||
-- gated by skillraceclassinfo_dbc (handled in 2026_05_09_00.sql),
|
||||
-- so the client/server agree on what's allowed.
|
||||
--
|
||||
-- Idempotent: deletes any pre-existing classMask=2048 rows first
|
||||
-- (class 12 owns this bitmask on Fractured) so the migration can
|
||||
-- replay cleanly on a partially-seeded DB.
|
||||
|
||||
DELETE FROM `playercreateinfo_skills` WHERE `classMask` = 2048;
|
||||
|
||||
INSERT INTO `playercreateinfo_skills`
|
||||
(`raceMask`, `classMask`, `skill`, `rank`, `comment`) VALUES
|
||||
-- Weapon proficiencies
|
||||
(0, 2048, 43, 0, 'Paragon - Swords'),
|
||||
(0, 2048, 44, 0, 'Paragon - Axes'),
|
||||
(0, 2048, 45, 0, 'Paragon - Bows'),
|
||||
(0, 2048, 46, 0, 'Paragon - Guns'),
|
||||
(0, 2048, 54, 0, 'Paragon - Maces'),
|
||||
(0, 2048, 55, 0, 'Paragon - Two-Handed Swords'),
|
||||
(0, 2048, 118, 0, 'Paragon - Dual Wield'),
|
||||
(0, 2048, 136, 0, 'Paragon - Staves'),
|
||||
(0, 2048, 160, 0, 'Paragon - Two-Handed Maces'),
|
||||
(0, 2048, 172, 0, 'Paragon - Two-Handed Axes'),
|
||||
(0, 2048, 173, 0, 'Paragon - Daggers'),
|
||||
(0, 2048, 176, 0, 'Paragon - Thrown'),
|
||||
(0, 2048, 226, 0, 'Paragon - Crossbows'),
|
||||
(0, 2048, 228, 0, 'Paragon - Wands'),
|
||||
(0, 2048, 229, 0, 'Paragon - Polearms'),
|
||||
(0, 2048, 473, 0, 'Paragon - Fist Weapons'),
|
||||
-- Armor proficiencies (Cloth is in a classMask=0 row already)
|
||||
(0, 2048, 293, 0, 'Paragon - Plate Mail'),
|
||||
(0, 2048, 413, 0, 'Paragon - Mail'),
|
||||
(0, 2048, 414, 0, 'Paragon - Leather'),
|
||||
(0, 2048, 433, 0, 'Paragon - Shield');
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,27 @@
|
||||
-- mod-paragon: Blood Elf "Arcane Torrent" uses three spell IDs in WotLK
|
||||
-- (28730 mana/casters, 25046 rogue energy, 50613 death knight runic power),
|
||||
-- all on racial skill line 756. Migration 2026_05_10_02 OR'd class 12 into
|
||||
-- every SkillLineAbility delta from patch-enUS-4, so Paragon Blood Elves
|
||||
-- auto-learned all three and the spellbook showed three identical entries.
|
||||
--
|
||||
-- Paragon should learn a single combined Arcane Torrent that refunds mana,
|
||||
-- energy, AND runic power -- whichever pool the character is using at the
|
||||
-- moment. We keep spell 28730 as the in-book entry for class 12 and attach
|
||||
-- the SpellScript spell_paragon_arcane_torrent (modules/mod-paragon/src/
|
||||
-- Paragon_SC.cpp) so casts by a Paragon also EnergizeBySpell energy + RP on
|
||||
-- top of the stock mana effect. Other classes' Blood Elves are unaffected.
|
||||
--
|
||||
-- IDs 13338 / 17510 match stock WotLK SkillLineAbility rows for spells 25046
|
||||
-- / 50613 on skill line 756.
|
||||
|
||||
UPDATE `skilllineability_dbc`
|
||||
SET `ClassMask` = `ClassMask` & ~2048
|
||||
WHERE `ID` IN (13338, 17510);
|
||||
|
||||
-- Bind spell_paragon_arcane_torrent (defined in Paragon_SC.cpp) to spell
|
||||
-- 28730. AC's `spell_script_names` is the standard mapping: script name on
|
||||
-- the right, spell id on the left. Idempotent via DELETE + INSERT.
|
||||
DELETE FROM `spell_script_names`
|
||||
WHERE `spell_id` = 28730 AND `ScriptName` = 'spell_paragon_arcane_torrent';
|
||||
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
|
||||
(28730, 'spell_paragon_arcane_torrent');
|
||||
@@ -0,0 +1,30 @@
|
||||
-- mod-paragon: extend ItemTemplate::AllowableClass to include class 12
|
||||
-- (Paragon, bit 1<<11 = 2048) for every class-restricted item.
|
||||
--
|
||||
-- Server-side, Player::CanUseItem (PlayerStorage.cpp) already short-
|
||||
-- circuits the AllowableClass check for class 12. That's enough for any
|
||||
-- code path the server controls (vendor list filter, AH "usable" filter,
|
||||
-- CanRollForItemInLFG, CanBuyItem). It is NOT enough on the 3.3.5 client:
|
||||
-- the WoW.exe binary independently pre-checks AllowableClass against the
|
||||
-- player's class on right-click of a bag item and refuses *locally* with
|
||||
-- the red "You can't use that item." text in UIErrorsFrame, never sending
|
||||
-- CMSG_USE_ITEM at all. Server logs stay silent; only client knows it
|
||||
-- refused.
|
||||
--
|
||||
-- Fix: OR class 12's bit into AllowableClass on every class-restricted
|
||||
-- row so the client engine's pre-check passes for Paragon. Other
|
||||
-- classes' bits are unchanged, so e.g. a warrior-only item is still
|
||||
-- warrior-only for everyone except Paragon. Items with AllowableClass
|
||||
-- == -1 ("all classes") or 0 ("no restriction recorded") already pass
|
||||
-- the client engine's check and are not touched.
|
||||
--
|
||||
-- After applying this migration the *client* still caches item info in
|
||||
-- Cache/<locale>/itemcache.wdb. Players who already inspected the item
|
||||
-- before the change must delete that file (or the whole Cache folder)
|
||||
-- and reconnect to repopulate it from the worldserver, otherwise the
|
||||
-- stale cached AllowableClass keeps the engine pre-check failing.
|
||||
|
||||
UPDATE `item_template`
|
||||
SET `AllowableClass` = `AllowableClass` | 2048
|
||||
WHERE `AllowableClass` > 0
|
||||
AND (`AllowableClass` & 2048) = 0;
|
||||
@@ -1,11 +0,0 @@
|
||||
-- Optional per-spell AE costs for Paragon spell purchases (.paragon learn).
|
||||
-- Apply to the *world* database.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `paragon_spell_ae_cost` (
|
||||
`spell_id` INT UNSIGNED NOT NULL,
|
||||
`ae_cost` SMALLINT UNSIGNED NOT NULL DEFAULT '2',
|
||||
PRIMARY KEY (`spell_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='mod-paragon: AE cost per spell';
|
||||
|
||||
-- Example (uncomment to use):
|
||||
-- INSERT INTO `paragon_spell_ae_cost` (`spell_id`, `ae_cost`) VALUES (55050, 2);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,15 +5,24 @@
|
||||
* so Paragon can reuse other classes' mechanics in narrowly scoped contexts.
|
||||
*/
|
||||
|
||||
#include "Chat.h"
|
||||
#include "Config.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureData.h"
|
||||
#include "GameTime.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "UnitDefines.h"
|
||||
#include "Config.h"
|
||||
#include "Log.h"
|
||||
#include "GameTime.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "SpellScript.h"
|
||||
#include "SpellScriptLoader.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class Paragon_PlayerScript : public PlayerScript
|
||||
@@ -30,7 +39,7 @@ public:
|
||||
{
|
||||
LOG_INFO("module", "[paragon] Paragon_PlayerScript registered "
|
||||
"(MultiResource.HasActivePowers={})",
|
||||
sConfigMgr->GetOption<bool>("Paragon.MultiResource.HasActivePowers", false));
|
||||
sConfigMgr->GetOption<bool>("Paragon.MultiResource.HasActivePowers", true));
|
||||
}
|
||||
|
||||
[[nodiscard]] Optional<bool> OnPlayerIsClass(Player const* player, Classes unitClass, ClassContext context) override
|
||||
@@ -38,27 +47,218 @@ public:
|
||||
if (!player || player->getClass() != CLASS_PARAGON)
|
||||
return std::nullopt;
|
||||
|
||||
// Death Knight rune / runic power ability stack (narrow on purpose).
|
||||
if (unitClass == CLASS_DEATH_KNIGHT && context == CLASS_CONTEXT_ABILITY)
|
||||
// ============================================================
|
||||
// Ability stack -- claim ALL nine vanilla classes.
|
||||
// ============================================================
|
||||
// CLASS_CONTEXT_ABILITY is read by every class-specific spell
|
||||
// gate in core / scripts: DK rune mechanics (Spell.cpp,
|
||||
// SpellEffects.cpp, spell_dk.cpp, SpellAuraEffects.cpp),
|
||||
// Warrior Titan's Grip / Bladestorm (Player.cpp 3783, 15432,
|
||||
// PlayerUpdates.cpp 1547), Paladin Rebuke (Player.cpp 15441),
|
||||
// Shaman dual-wield bookkeeping (Player.cpp 5028), Hunter pet
|
||||
// / Hunter's Mark gates (spell_item.cpp 3718), Druid Insect
|
||||
// Swarm / Wild Growth (SpellAuraEffects.cpp 2153, 2232),
|
||||
// Priest Spirit of Redemption out-of-bounds check (Unit.cpp
|
||||
// 14238), Rogue pickpocketing (LootHandler.cpp 86/165/385,
|
||||
// Vehicle.cpp 80). Paragon learns abilities from every class
|
||||
// through Character Advancement, so claiming all of them lets
|
||||
// every gated spell script execute its class-specific branch
|
||||
// for our players. The only downside is double-pathed scripts
|
||||
// (e.g. a spell with both warrior and rogue branches) will
|
||||
// pick whichever the script tests first -- acceptable.
|
||||
if (context == CLASS_CONTEXT_ABILITY)
|
||||
return true;
|
||||
|
||||
// Warrior ability stack: enables warrior-spec ability gates anywhere
|
||||
// they're checked. None of the currently-traced sites in core/scripts
|
||||
// gate on (CLASS_WARRIOR, CLASS_CONTEXT_ABILITY), so this is a safe
|
||||
// forward-compatible claim. Rage generation itself is gated on
|
||||
// HasActivePowerType(POWER_RAGE) and is wired below.
|
||||
if (unitClass == CLASS_WARRIOR && context == CLASS_CONTEXT_ABILITY)
|
||||
return true;
|
||||
|
||||
// Reactive melee states: Overpower-on-dodge (warrior), Counterattack window (hunter).
|
||||
// We intentionally do NOT claim CLASS_ROGUE here: that context skips the generic
|
||||
// AURA_STATE_DEFENSE update on dodge (Riposte path) in Unit::ProcDamageAndSpellFor.
|
||||
// ============================================================
|
||||
// Reactive melee states.
|
||||
// ============================================================
|
||||
// Warrior dodge -> AURA_STATE_DEFENSE (Overpower window).
|
||||
// Hunter parry -> AURA_STATE_HUNTER_PARRY (Counterattack).
|
||||
// We intentionally do NOT claim CLASS_ROGUE here:
|
||||
// Unit::ProcDamageAndSpellFor (Unit.cpp 12824) skips the
|
||||
// generic AURA_STATE_DEFENSE update on dodge for rogues so
|
||||
// Riposte can take over. Claiming rogue would silently kill
|
||||
// Overpower for Paragon, and Riposte already works for us via
|
||||
// the warrior-style state we already grant.
|
||||
if (context == CLASS_CONTEXT_ABILITY_REACTIVE)
|
||||
{
|
||||
if (unitClass == CLASS_WARRIOR || unitClass == CLASS_HUNTER)
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Pet ownership contexts.
|
||||
// ============================================================
|
||||
// CLASS_CONTEXT_PET is read by Pet::AddToWorld, Pet::CreateBase
|
||||
// AtCreatureInfo, Pet::InitStatsForLevel (twice -- the
|
||||
// MAX_PET_TYPE bootstrap branch and the per-class attack-time
|
||||
// scaling), Pet::IsPermanentPetFor, Player::SummonPet,
|
||||
// Player::CanResummonPet, Spell::EffectTameCreature,
|
||||
// SpellEffects.cpp (CreateTamedPet debug effects, Eyes of the
|
||||
// Beast), spell_generic.cpp 1760 (charm-as-pet conversion),
|
||||
// and PlayerGossip.cpp's hunter stable check.
|
||||
//
|
||||
// The cleanest disambiguation is by the *active pet's* shape:
|
||||
// HUNTER_PET -> hunter (beast tame)
|
||||
// SUMMON_PET + DEMON type -> warlock (Imp/VW/Succ/...)
|
||||
// SUMMON_PET + UNDEAD type -> DK ghoul / Army of Dead
|
||||
// SUMMON_PET + ELEMENTAL type -> mage water / shaman fire
|
||||
// For HUNTER specifically the no-pet case is also claimed so
|
||||
// Tame Beast's EffectTameCreature gate passes during cast.
|
||||
if (context == CLASS_CONTEXT_PET)
|
||||
{
|
||||
Pet const* activePet = const_cast<Player*>(player)->GetPet();
|
||||
|
||||
// Hunter beast: claim during taming OR when a HUNTER_PET is
|
||||
// already active. This is what makes Tame Beast / Call Pet
|
||||
// / pet stable / Counterattack pet aura feedback work.
|
||||
if (unitClass == CLASS_HUNTER)
|
||||
{
|
||||
if (!activePet || activePet->getPetType() == HUNTER_PET)
|
||||
return true;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// All other classes only claim when an active SUMMON_PET is
|
||||
// present. We then disambiguate by the creature's type
|
||||
// because warlock / DK / mage / shaman all use SUMMON_PET.
|
||||
if (!activePet || activePet->getPetType() != SUMMON_PET)
|
||||
return std::nullopt;
|
||||
|
||||
CreatureTemplate const* tmpl = activePet->GetCreatureTemplate();
|
||||
if (!tmpl)
|
||||
return std::nullopt;
|
||||
|
||||
switch (unitClass)
|
||||
{
|
||||
case CLASS_WARLOCK:
|
||||
// Drives Master Demonologist / Demonic Knowledge /
|
||||
// Demonic Pact propagation, last-pet-spell tracking
|
||||
// (Pet.cpp 112), and IsPermanentPetFor (Pet.cpp
|
||||
// 2288) so demon pets persist across logins.
|
||||
if (tmpl->type == CREATURE_TYPE_DEMON)
|
||||
return true;
|
||||
break;
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
// Risen Ghoul + Army of the Dead. Player.cpp 14354
|
||||
// and Pet.cpp 243 / 1046 / 2290 read this; without
|
||||
// it the ghoul is invisible to the owner mid-load
|
||||
// and ScriptedAI hooks on the ghoul mis-route.
|
||||
if (tmpl->type == CREATURE_TYPE_UNDEAD)
|
||||
return true;
|
||||
break;
|
||||
case CLASS_MAGE:
|
||||
// Glyph-of-Eternal-Water permanent Water Elemental
|
||||
// (entry 510, 37994). Used by Pet.cpp 1047/2292.
|
||||
if (tmpl->type == CREATURE_TYPE_ELEMENTAL)
|
||||
return true;
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
// Fire Elemental / Earth Elemental. The base
|
||||
// engine spawns these as creatures rather than
|
||||
// proper Pet instances in most code paths, so the
|
||||
// claim mostly matters for the Pet.cpp 1045 stat
|
||||
// bootstrap when one is loaded as a SUMMON_PET.
|
||||
if (tmpl->type == CREATURE_TYPE_ELEMENTAL)
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Warlock pet-charm context (Enslave Demon -- Unit.cpp 14828,
|
||||
// 14894, 15025). Without this claim, charming a demon as a
|
||||
// Paragon doesn't get the warlock-flavor charm semantics
|
||||
// (faction-set-on-charm, action-bar layout, charm-break logic).
|
||||
if (unitClass == CLASS_WARLOCK && context == CLASS_CONTEXT_PET_CHARM)
|
||||
return true;
|
||||
|
||||
// ============================================================
|
||||
// Equipment contexts.
|
||||
// ============================================================
|
||||
// CLASS_CONTEXT_EQUIP_RELIC: PlayerStorage.cpp 224-240 +
|
||||
// 2475-2493. Routes Librams/Idols/Totems/Misc/Sigils into
|
||||
// EQUIPMENT_SLOT_RANGED for the matching class. Claim every
|
||||
// relic-bearing class so a Paragon can drop any of them into
|
||||
// the ranged slot.
|
||||
if (context == CLASS_CONTEXT_EQUIP_RELIC)
|
||||
{
|
||||
switch (unitClass)
|
||||
{
|
||||
case CLASS_PALADIN:
|
||||
case CLASS_DRUID:
|
||||
case CLASS_SHAMAN:
|
||||
case CLASS_WARLOCK:
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// CLASS_CONTEXT_EQUIP_ARMOR_CLASS: PlayerStorage.cpp 2326,
|
||||
// 2330, 2503-2523. At level 40 each class auto-learns its
|
||||
// top armor proficiency. Paragon should pick up plate (via
|
||||
// paladin/DK), shields (paladin/warrior/shaman), mail
|
||||
// (hunter/shaman), and leather (rogue) so the level-40 train
|
||||
// event grants Paragon full proficiency and we don't have to
|
||||
// hand-curate it through the Paragon proficiency SQL.
|
||||
if (context == CLASS_CONTEXT_EQUIP_ARMOR_CLASS)
|
||||
{
|
||||
switch (unitClass)
|
||||
{
|
||||
case CLASS_PALADIN:
|
||||
case CLASS_WARRIOR:
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
case CLASS_HUNTER:
|
||||
case CLASS_SHAMAN:
|
||||
case CLASS_DRUID:
|
||||
case CLASS_ROGUE:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// CLASS_CONTEXT_EQUIP_SHIELDS: PlayerStorage.cpp 2467-2469.
|
||||
// Lets a Paragon equip shields without a paladin/warrior/
|
||||
// shaman skill gate.
|
||||
if (context == CLASS_CONTEXT_EQUIP_SHIELDS)
|
||||
{
|
||||
switch (unitClass)
|
||||
{
|
||||
case CLASS_PALADIN:
|
||||
case CLASS_WARRIOR:
|
||||
case CLASS_SHAMAN:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// CLASS_CONTEXT_WEAPON_SWAP: PlayerStorage.cpp 1920, 2838 --
|
||||
// rogue uses cooldown spell 6123 instead of 6119 on weapon
|
||||
// swap (Quick Draw / Combat Potency interactions). Claim
|
||||
// rogue so Paragon picks up the same cooldown spell.
|
||||
if (context == CLASS_CONTEXT_WEAPON_SWAP && unitClass == CLASS_ROGUE)
|
||||
return true;
|
||||
|
||||
// ============================================================
|
||||
// Contexts we DELIBERATELY DO NOT claim:
|
||||
// ============================================================
|
||||
// CLASS_CONTEXT_STATS -- Paragon has its own STR/AGI->AP and
|
||||
// INT/SPI->SP curves wired in StatSystem.cpp's CLASS_PARAGON
|
||||
// branch (level*2 + STR + AGI - 20 etc.). Claiming any
|
||||
// vanilla class here would override our curves with theirs.
|
||||
//
|
||||
// CLASS_CONTEXT_INIT, _TELEPORT, _QUEST, _TAXI, _SKILL,
|
||||
// _GRAVEYARD, _CLASS_TRAINER, _TALENT_POINT_CALC -- all
|
||||
// used by DK Ebon Hold / druid Moonglade starting-zone
|
||||
// scripts. Paragon doesn't go through those zones and we
|
||||
// don't want our players bound to Acherus or trapped in
|
||||
// the DK starting quest gates.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -70,7 +270,7 @@ public:
|
||||
if (power == POWER_RUNIC_POWER || power == POWER_RUNE)
|
||||
return true;
|
||||
|
||||
if (sConfigMgr->GetOption<bool>("Paragon.MultiResource.HasActivePowers", false))
|
||||
if (sConfigMgr->GetOption<bool>("Paragon.MultiResource.HasActivePowers", true))
|
||||
{
|
||||
switch (power)
|
||||
{
|
||||
@@ -184,6 +384,45 @@ public:
|
||||
{
|
||||
player->ResyncRunes(MAX_RUNES);
|
||||
st.lastReadyMask = readyMask;
|
||||
|
||||
// Authoritative rune CD pump (PARAA "R RUNES cd0 cd1 ... cd5",
|
||||
// ms remaining per slot, 0 = ready). The 3.3.5 client engine
|
||||
// class-gates SMSG_RESYNC_RUNES / SMSG_SPELL_GO RUNE_LIST to DK,
|
||||
// so the Paragon RuneFrame sim drives the visual entirely off
|
||||
// COMBAT_LOG_EVENT_UNFILTERED:SPELL_CAST_SUCCESS. The combat log
|
||||
// arrives ~100–200ms after the server already started the
|
||||
// cooldown, so the client's local timer trails the server. When
|
||||
// the user spams a rune spell, the server's slot refreshes
|
||||
// first, accepts the next cast, but the client UI still shows
|
||||
// CD remaining → "leak-through" past a greyed icon. Pushing the
|
||||
// actual remaining ms on every mask transition keeps the
|
||||
// visual locked to server state.
|
||||
std::string body = "R RUNES";
|
||||
for (uint8 i = 0; i < MAX_RUNES; ++i)
|
||||
body += " " + std::to_string(player->GetRuneCooldown(i));
|
||||
std::string const payload = std::string(kParagonAddonPrefix) + "\t" + body;
|
||||
WorldPacket runePkt;
|
||||
ChatHandler::BuildChatPacket(runePkt, CHAT_MSG_WHISPER, LANG_ADDON, player, player, payload);
|
||||
player->SendDirectMessage(&runePkt);
|
||||
}
|
||||
|
||||
// Combo point pump: the 3.3.5 client engine class-gates SMSG_UPDATE_COMBO_POINTS
|
||||
// to rogue / druid, so the Paragon UI sim never sees CP changes from
|
||||
// Honor Among Thieves / Mutilate / etc. via either the engine state or
|
||||
// the client-side combat-log inference (HAT's 51699 trigger fires with a
|
||||
// null target and doesn't always emit SPELL_CAST_SUCCESS in the log).
|
||||
// Push the count over PARAA whenever it changes; the addon's combo
|
||||
// simulator listens for "R CP <n>" and overwrites paragonCP, so the
|
||||
// ComboFrame on the target frame paints reliably.
|
||||
int8 const cp = player->GetComboPoints();
|
||||
if (cp != st.lastCp)
|
||||
{
|
||||
std::string const payload = std::string(kParagonAddonPrefix) + "\t"
|
||||
+ fmt::format("R CP {}", int32(cp));
|
||||
WorldPacket data;
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, LANG_ADDON, player, player, payload);
|
||||
player->SendDirectMessage(&data);
|
||||
st.lastCp = cp;
|
||||
}
|
||||
|
||||
if (!sConfigMgr->GetOption<bool>("Paragon.Diag.RuneTrace", false))
|
||||
@@ -214,14 +453,69 @@ private:
|
||||
struct ParagonRuneSyncState
|
||||
{
|
||||
uint8 lastReadyMask{0xFFu}; // sentinel: no prior snapshot
|
||||
int8 lastCp{-1}; // sentinel: no prior snapshot
|
||||
};
|
||||
|
||||
static constexpr char const* kParagonAddonPrefix = "PARAA";
|
||||
|
||||
static std::unordered_map<ObjectGuid, ParagonRuneSyncState> runeSyncByGuid;
|
||||
};
|
||||
|
||||
std::unordered_map<ObjectGuid, Paragon_PlayerScript::ParagonRuneSyncState> Paragon_PlayerScript::runeSyncByGuid;
|
||||
|
||||
// Arcane Torrent (28730) for Paragon: Blood Elf racial skill line 756 has
|
||||
// three Arcane Torrent variants in stock WotLK (28730 mana, 25046 rogue
|
||||
// energy, 50613 DK runic power). For Paragon Blood Elves we keep only 28730
|
||||
// (see migration 2026_05_10_03.sql) and turn it into a "combined" version:
|
||||
// the stock spell already silences nearby enemies and energizes mana via its
|
||||
// own effects; this script adds energy, rage, and runic power energize on
|
||||
// top when the caster is class 12, so a single button refunds whichever
|
||||
// resource pool the player is actually using. Non-Paragon casters are
|
||||
// untouched and keep learning their stock racial variant.
|
||||
class spell_paragon_arcane_torrent : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_paragon_arcane_torrent);
|
||||
|
||||
void HandleAfterCast()
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || !caster->IsPlayer())
|
||||
return;
|
||||
|
||||
Player* player = caster->ToPlayer();
|
||||
if (player->getClass() != CLASS_PARAGON)
|
||||
return;
|
||||
|
||||
// Stock energize amounts from spell_dbc:
|
||||
// 25046 Arcane Torrent (Energy) -> 15 energy
|
||||
// 50613 Arcane Torrent (Runic Power) -> 15 displayed RP (= 150
|
||||
// internal; AC stores RP scaled 10x, see Player::SetMaxPower
|
||||
// POWER_RUNIC_POWER, 1000).
|
||||
// Rage uses the same 10x internal scaling as runic power (see
|
||||
// Player.cpp:Regenerate where rage decay is `-20` for "2 rage by
|
||||
// tick"), so 15 displayed rage = 150 internal.
|
||||
// ModifyPower no-ops on pools the player has no max for, so this is
|
||||
// safe even before the Paragon picks up energy/rage/RP abilities.
|
||||
constexpr int32 kEnergyGain = 15;
|
||||
constexpr int32 kRageGain = 150;
|
||||
constexpr int32 kRunicPowerGain = 150;
|
||||
|
||||
SpellInfo const* spellInfo = GetSpellInfo();
|
||||
uint32 const spellId = spellInfo ? spellInfo->Id : 28730u;
|
||||
|
||||
caster->EnergizeBySpell(player, spellId, kEnergyGain, POWER_ENERGY);
|
||||
caster->EnergizeBySpell(player, spellId, kRageGain, POWER_RAGE);
|
||||
caster->EnergizeBySpell(player, spellId, kRunicPowerGain, POWER_RUNIC_POWER);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterCast += SpellCastFn(spell_paragon_arcane_torrent::HandleAfterCast);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_paragon()
|
||||
{
|
||||
new Paragon_PlayerScript();
|
||||
RegisterSpellScript(spell_paragon_arcane_torrent);
|
||||
}
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
# Clone Dawnforger/Fractured and omit Docker-only paths. Use when this script is
|
||||
# already on disk (e.g. scp). Otherwise: git clone … && cd Fractured && bash scripts/vps-sparse-checkout-no-docker.sh
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/vps-clone-without-docker.sh /path/to/Fractured git@github.com:Dawnforger/Fractured.git
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TARGET="${1:?usage: $0 /path/to/Fractured <git-remote-url>}"
|
||||
REMOTE="${2:?usage: $0 /path/to/Fractured <git-remote-url>}"
|
||||
|
||||
if [[ -e "$TARGET" ]]; then
|
||||
echo "error: $TARGET already exists; remove it or pick another path." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$TARGET")"
|
||||
git clone "$REMOTE" "$TARGET"
|
||||
|
||||
cd "$TARGET"
|
||||
if [[ ! -f scripts/vps-sparse-checkout-no-docker.sh ]]; then
|
||||
echo "error: clone missing scripts/vps-sparse-checkout-no-docker.sh — pull latest main." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bash scripts/vps-sparse-checkout-no-docker.sh
|
||||
|
||||
echo "Done. Next: docs/DEPLOY_LINUX_VPS.md"
|
||||
Executable
+336
@@ -0,0 +1,336 @@
|
||||
#!/usr/bin/env bash
|
||||
# Collect VPS evidence for Paragon / DBUpdater / binary staleness triage.
|
||||
# Run ON the VPS (Linux). Safe: read-only; does not restart services.
|
||||
#
|
||||
# Usage (from clone):
|
||||
# bash scripts/vps-paragon-diagnostics.sh
|
||||
#
|
||||
# Optional environment:
|
||||
# FRACTURED_REPO — absolute path to Fractured git root (default: parent of scripts/)
|
||||
# FRACTURED_WS_BIN — path to worldserver binary (default: auto-detect)
|
||||
# FRACTURED_WORLDSERVER_CONF — path to worldserver.conf (default: guess from BIN + common layouts)
|
||||
# FRACTURED_SYSTEMD_UNITS — space-separated units to try (default: "fractured-world worldserver ac-worldserver")
|
||||
# FRACTURED_MYSQL — prefix to invoke mysql, e.g. 'mysql -uacore -pacore -h127.0.0.1'
|
||||
# (default Fractured local DB user/password are often both "acore"; use ~/.my.cnf if you prefer not to pass -p on the command line)
|
||||
# If unset, SQL blocks are printed for manual copy-paste only.
|
||||
# FRACTURED_SPELL_IDS — space-separated spell IDs for spell_dbc spot-check (defaults to common DK rune spenders)
|
||||
# FRACTURED_DIAG_OUTPUT — full log file path (default: <repo>/var/vps-paragon-diagnostics-last.txt)
|
||||
#
|
||||
# All output is mirrored to the log file (tee) while still printing to the terminal.
|
||||
# Default path lives under var/ (gitignored in this repo). Open that file in Cursor,
|
||||
# scp it down, or: git add -f var/vps-paragon-diagnostics-last.txt if you intend to commit it.
|
||||
|
||||
set -u
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO="${FRACTURED_REPO:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
||||
|
||||
DIAG_OUT="${FRACTURED_DIAG_OUTPUT:-$REPO/var/vps-paragon-diagnostics-last.txt}"
|
||||
mkdir -p "$(dirname "$DIAG_OUT")"
|
||||
exec > >(tee "$DIAG_OUT") 2>&1
|
||||
echo "Logging to: $DIAG_OUT"
|
||||
|
||||
hr() { printf '\n%s\n' "================================================================================"; }
|
||||
sub() { printf '\n-- %s\n' "$1"; }
|
||||
|
||||
detect_worldserver_bin() {
|
||||
local bin="" es path u units
|
||||
if [[ -n "${FRACTURED_WS_BIN:-}" ]]; then
|
||||
readlink -f "$FRACTURED_WS_BIN" 2>/dev/null && return
|
||||
echo "$FRACTURED_WS_BIN"
|
||||
return
|
||||
fi
|
||||
|
||||
units="${FRACTURED_SYSTEMD_UNITS:-fractured-world worldserver ac-worldserver}"
|
||||
for u in $units; do
|
||||
if systemctl is-active --quiet "$u" 2>/dev/null || systemctl is-enabled --quiet "$u" 2>/dev/null; then
|
||||
es=$(systemctl show "$u" -p ExecStart --value 2>/dev/null || true)
|
||||
if [[ -n "$es" ]]; then
|
||||
if [[ "$es" == \{*path=* ]]; then
|
||||
path=$(printf '%s' "$es" | sed -n 's/.*path=\([^;]*\).*/\1/p')
|
||||
else
|
||||
path=$(printf '%s' "$es" | awk '{print $1}' | sed 's/^path=//')
|
||||
fi
|
||||
if [[ -n "$path" && -x "$path" ]]; then
|
||||
readlink -f "$path" 2>/dev/null && return
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
local pid
|
||||
pid=$(pgrep -xo worldserver 2>/dev/null || true)
|
||||
if [[ -n "$pid" ]]; then
|
||||
readlink -f "/proc/$pid/exe" 2>/dev/null && return
|
||||
fi
|
||||
|
||||
if command -v worldserver >/dev/null 2>&1; then
|
||||
readlink -f "$(command -v worldserver)" 2>/dev/null && return
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
guess_worldserver_conf() {
|
||||
local bin="$1"
|
||||
local d cands=()
|
||||
[[ -z "$bin" ]] && return
|
||||
d=$(dirname "$bin")
|
||||
cands+=("$d/../etc/worldserver.conf")
|
||||
cands+=("$d/../../etc/worldserver.conf")
|
||||
cands+=("$HOME/azeroth-server/etc/worldserver.conf")
|
||||
cands+=("$HOME/env/dist/etc/worldserver.conf")
|
||||
for f in "${cands[@]}"; do
|
||||
f=$(readlink -f "$f" 2>/dev/null || true)
|
||||
if [[ -n "$f" && -f "$f" ]]; then
|
||||
echo "$f"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
binary_strings_paths() {
|
||||
local ws="$1"
|
||||
[[ -z "$ws" || ! -f "$ws" ]] && return
|
||||
strings "$ws" 2>/dev/null | grep -iE '/(home|root|opt|srv|var)[^[:space:]]*/(Fractured|fractured|azeroth|AzerothCore|acore)' | sort -u | head -40
|
||||
}
|
||||
|
||||
hr
|
||||
echo "Fractured Paragon / native VPS diagnostics"
|
||||
echo "Date (UTC): $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||||
echo "Repo (expected): $REPO"
|
||||
|
||||
sub "1A — worldserver binary"
|
||||
WS=$(detect_worldserver_bin || true)
|
||||
if [[ -z "$WS" ]]; then
|
||||
echo "ERROR: Could not find worldserver. Set FRACTURED_WS_BIN=/full/path/to/worldserver and re-run."
|
||||
else
|
||||
echo "Binary: $WS"
|
||||
if stat -c 'binary mtime: %y' "$WS" 2>/dev/null; then
|
||||
:
|
||||
else
|
||||
stat -f 'binary mtime: %Sm' -t '%Y-%m-%d %H:%M:%S %z' "$WS" 2>/dev/null || stat "$WS"
|
||||
fi
|
||||
fi
|
||||
|
||||
sub "1B — repo HEAD + Paragon_Essence.cpp mtime"
|
||||
if [[ -d "$REPO/.git" ]]; then
|
||||
(cd "$REPO" && git log -1 --format='HEAD commit: %h %ci %s')
|
||||
else
|
||||
echo "WARN: not a git repo: $REPO (set FRACTURED_REPO)"
|
||||
fi
|
||||
PE="$REPO/modules/mod-paragon/src/Paragon_Essence.cpp"
|
||||
if [[ -f "$PE" ]]; then
|
||||
if stat -c 'Paragon_Essence.cpp mtime: %y' "$PE" 2>/dev/null; then
|
||||
:
|
||||
else
|
||||
stat -f 'Paragon_Essence.cpp mtime: %Sm' -t '%Y-%m-%d %H:%M:%S %z' "$PE" 2>/dev/null || stat "$PE"
|
||||
fi
|
||||
else
|
||||
echo "WARN: missing $PE"
|
||||
fi
|
||||
|
||||
sub "1C — strings heuristics (0 can mean stripped binary — use 1A+1B)"
|
||||
if [[ -n "$WS" && -f "$WS" ]]; then
|
||||
c1=$(strings "$WS" 2>/dev/null | grep -c 'CLASS_PARAGON' || true)
|
||||
c2=$(strings "$WS" 2>/dev/null | grep -c 'C BUILD SAVE_CURRENT' || true)
|
||||
c3=$(strings "$WS" 2>/dev/null | grep -c 'character_paragon_build_share_archive' || true)
|
||||
echo "CLASS_PARAGON count: $c1"
|
||||
echo "C BUILD SAVE_CURRENT count: $c2"
|
||||
echo "character_paragon_build_share_archive count: $c3"
|
||||
else
|
||||
echo "(skipped — no binary)"
|
||||
fi
|
||||
|
||||
sub "1D — binary fingerprint (compare sha256 across dev vs VPS)"
|
||||
if [[ -n "$WS" && -f "$WS" ]]; then
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "$WS"
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
shasum -a 256 "$WS"
|
||||
else
|
||||
echo "(no sha256sum — install coreutils)"
|
||||
fi
|
||||
echo "Embedded revision / version strings (first matches):"
|
||||
strings "$WS" 2>/dev/null | grep -iE 'azerothcore|revision|git|commit|build.*20[0-9]{2}' | head -25 || echo "(none matched)"
|
||||
else
|
||||
echo "(skipped — no binary)"
|
||||
fi
|
||||
|
||||
CONF="${FRACTURED_WORLDSERVER_CONF:-}"
|
||||
if [[ -z "$CONF" && -n "$WS" ]]; then
|
||||
CONF=$(guess_worldserver_conf "$WS")
|
||||
fi
|
||||
|
||||
sub "2B — worldserver.conf (updater / source / rates / paragon)"
|
||||
if [[ -n "$CONF" && -f "$CONF" ]]; then
|
||||
echo "Using conf: $CONF"
|
||||
grep -E '^SourceDirectory|^Updates\.EnableDatabases|^Updates\.AutoSetup|^[[:space:]]*SourceDirectory|^[[:space:]]*Updates\.EnableDatabases|^[[:space:]]*Updates\.AutoSetup' "$CONF" 2>/dev/null || echo "(no matching lines or unreadable)"
|
||||
echo "--- Rate.RunicPower (if set) ---"
|
||||
grep -iE '^Rate\.RunicPower|^[[:space:]]*Rate\.RunicPower' "$CONF" 2>/dev/null || echo "(not set — server uses default)"
|
||||
echo "--- Paragon.* module options (if any) ---"
|
||||
grep -iE '^Paragon\.|^[[:space:]]*Paragon\.' "$CONF" 2>/dev/null || echo "(no Paragon.* keys in worldserver.conf — check etc/modules/mod_paragon.conf)"
|
||||
else
|
||||
echo "WARN: worldserver.conf not found. Set FRACTURED_WORLDSERVER_CONF=/path/to/worldserver.conf"
|
||||
fi
|
||||
|
||||
if [[ -n "$WS" && -f "$WS" ]]; then
|
||||
ETCGuess=$(readlink -f "$(dirname "$WS")/../etc" 2>/dev/null || true)
|
||||
MPC="$ETCGuess/modules/mod_paragon.conf"
|
||||
if [[ -f "$MPC" ]]; then
|
||||
sub "2B2 — mod_paragon.conf Paragon.* toggles (non-comment)"
|
||||
grep -E '^Paragon\.' "$MPC" 2>/dev/null | head -40 || echo "(no uncommented Paragon.* lines)"
|
||||
fi
|
||||
fi
|
||||
|
||||
sub "2A — path-like strings from binary (candidate source roots)"
|
||||
if [[ -n "$WS" && -f "$WS" ]]; then
|
||||
binary_strings_paths "$WS" || true
|
||||
else
|
||||
echo "(skipped)"
|
||||
fi
|
||||
|
||||
sub "Resolved source root for 2D"
|
||||
RESOLVED=""
|
||||
if [[ -n "$CONF" && -f "$CONF" ]]; then
|
||||
sd=$(awk -F= '/^[[:space:]]*SourceDirectory[[:space:]]*=/ {
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2);
|
||||
gsub(/^["'\'']|["'\'']$/, "", $2);
|
||||
print $2; exit }' "$CONF" 2>/dev/null || true)
|
||||
if [[ -n "${sd:-}" ]]; then
|
||||
RESOLVED="$sd"
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$RESOLVED" ]]; then
|
||||
RESOLVED="$REPO"
|
||||
fi
|
||||
echo "Using RESOLVED=$RESOLVED (from SourceDirectory if set in conf, else FRACTURED_REPO)"
|
||||
|
||||
sub "2D — Paragon SQL dirs under RESOLVED"
|
||||
for subdir in \
|
||||
"$RESOLVED/modules/mod-paragon/data/sql/db-world/updates/" \
|
||||
"$RESOLVED/modules/mod-paragon/data/sql/db-characters/updates/"; do
|
||||
if [[ -d "$subdir" ]]; then
|
||||
echo "Listing: $subdir"
|
||||
ls -la "$subdir" 2>/dev/null | tail -15
|
||||
else
|
||||
echo "MISSING: $subdir"
|
||||
fi
|
||||
done
|
||||
|
||||
sub "CMake build dir hints (common Fractured layouts)"
|
||||
for cand in "$REPO/var/build/obj" "$REPO/build" "$REPO/../build"; do
|
||||
if [[ -f "$cand/CMakeCache.txt" ]]; then
|
||||
echo "Found CMakeCache: $cand/CMakeCache.txt"
|
||||
grep -E '^CMAKE_HOME_DIRECTORY:|^MODULES:|^CMAKE_INSTALL_PREFIX:' "$cand/CMakeCache.txt" 2>/dev/null | head -5
|
||||
fi
|
||||
done
|
||||
|
||||
sub "DATABASE — updates rows (2026_05_10 / paragon)"
|
||||
SQL_WORLD=$(cat <<'EOS'
|
||||
SELECT name, hash, speed FROM updates
|
||||
WHERE name LIKE '2026_05_10%' OR name LIKE '%paragon%'
|
||||
ORDER BY name DESC LIMIT 30;
|
||||
EOS
|
||||
)
|
||||
SQL_CHAR="$SQL_WORLD"
|
||||
|
||||
if [[ -n "${FRACTURED_MYSQL:-}" ]]; then
|
||||
echo "--- acore_world ---"
|
||||
$FRACTURED_MYSQL acore_world -e "$SQL_WORLD" || echo "(mysql failed for acore_world)"
|
||||
echo "--- acore_characters ---"
|
||||
$FRACTURED_MYSQL acore_characters -e "$SQL_CHAR" || echo "(mysql failed for acore_characters)"
|
||||
|
||||
sub "DATABASE — DBC parity for runes / Paragon (acore_world)"
|
||||
# Common DK rune spenders (WotLK). Override: export FRACTURED_SPELL_IDS='45477 45462'
|
||||
SPELL_IDS="${FRACTURED_SPELL_IDS:-45477 45462 49923 55050 56815}"
|
||||
IDS_CSV=$(echo "$SPELL_IDS" | tr ' ' ',')
|
||||
echo "--- spell_dbc table size (world DB overrides; 0 rows = all spells from disk DBC only) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "SELECT COUNT(*) AS spell_dbc_rows FROM spell_dbc;" 2>/dev/null || echo "(spell_dbc missing or no access)"
|
||||
echo "--- acore_world.version (last core revision written by worldserver) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "SELECT * FROM version LIMIT 5;" 2>/dev/null || echo "(version table missing?)"
|
||||
|
||||
echo "--- chrclasses_dbc class 6 + 12 (DisplayPower: 0=mana, 5=POWER_RUNE in AC) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT ID, DisplayPower, Name_Lang_enUS FROM chrclasses_dbc WHERE ID IN (6,12);
|
||||
" 2>/dev/null || echo "(query failed — chrclasses_dbc missing?)"
|
||||
echo "Note: If only ID=12 appears, class 6 (DK) is not overridden in DB — loaded from disk DBC (normal)."
|
||||
|
||||
echo "--- spell_dbc: are sample DK spells overridden in DB? ---"
|
||||
spell_sample_n=$($FRACTURED_MYSQL acore_world -N -B -e \
|
||||
"SELECT COUNT(*) FROM spell_dbc WHERE ID IN ($IDS_CSV);" 2>/dev/null || echo 0)
|
||||
echo "Row count in spell_dbc for sample IDs ($SPELL_IDS): ${spell_sample_n:-0}"
|
||||
if [[ "${spell_sample_n:-0}" == "0" ]]; then
|
||||
echo "=> 0 means those spells use on-disk Spell.dbc only; the sample block below will be empty (not an error)."
|
||||
fi
|
||||
echo "--- spell_dbc sample (PowerType 5 = POWER_RUNE in AC) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT ID, PowerType, ManaCost, RuneCostID FROM spell_dbc WHERE ID IN ($IDS_CSV);
|
||||
" 2>/dev/null || echo "(query failed — spell_dbc missing or wrong schema)"
|
||||
echo "--- spellrunecost join for sample IDs (empty if no spell_dbc rows above) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT s.ID AS spell_id, s.PowerType, s.RuneCostID, r.Blood, r.Unholy, r.Frost, r.RunicPower
|
||||
FROM spell_dbc s
|
||||
LEFT JOIN spellrunecost_dbc r ON r.ID = s.RuneCostID
|
||||
WHERE s.ID IN ($IDS_CSV);
|
||||
" 2>/dev/null || echo "(join failed — check spellrunecost_dbc)"
|
||||
|
||||
echo "--- spell_dbc suspicious overrides: RuneCostID>0 but PowerType!=5 (can break rune checks) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT ID, PowerType, ManaCost, RuneCostID FROM spell_dbc
|
||||
WHERE RuneCostID > 0 AND PowerType <> 5
|
||||
ORDER BY ID LIMIT 40;
|
||||
" 2>/dev/null || echo "(query failed)"
|
||||
echo "Compare counts/IDs to dev: unexpected rows here warrant a DB diff."
|
||||
|
||||
echo "--- spell_dbc POWER_RUNE (5) spells with RuneCostID (sample) ---"
|
||||
$FRACTURED_MYSQL acore_world -e "
|
||||
SELECT ID, PowerType, RuneCostID FROM spell_dbc
|
||||
WHERE PowerType = 5 AND RuneCostID > 0
|
||||
ORDER BY ID LIMIT 15;
|
||||
" 2>/dev/null || echo "(query failed)"
|
||||
else
|
||||
echo "FRACTURED_MYSQL not set — run manually (example: export FRACTURED_MYSQL='mysql -uUSER -hHOST')"
|
||||
echo "acore_world:"
|
||||
echo "$SQL_WORLD"
|
||||
echo "acore_characters:"
|
||||
echo "$SQL_CHAR"
|
||||
echo ""
|
||||
echo "Optional DBC parity (acore_world) — run after connecting:"
|
||||
echo " SELECT ID, DisplayPower, Name_Lang_enUS FROM chrclasses_dbc WHERE ID IN (6,12);"
|
||||
echo " SELECT ID, PowerType, ManaCost, RuneCostID FROM spell_dbc WHERE ID IN (45477,45462,49923,55050,56815);"
|
||||
echo " SELECT s.ID, s.RuneCostID, r.Blood, r.Unholy, r.Frost, r.RunicPower FROM spell_dbc s"
|
||||
echo " LEFT JOIN spellrunecost_dbc r ON r.ID = s.RuneCostID WHERE s.ID IN (45477,45462,49923,55050,56815);"
|
||||
fi
|
||||
|
||||
sub "mod_paragon.conf vs .dist (install etc)"
|
||||
ETC=""
|
||||
if [[ -n "$WS" ]]; then
|
||||
ETC=$(readlink -f "$(dirname "$WS")/../etc" 2>/dev/null || true)
|
||||
fi
|
||||
if [[ -z "$ETC" || ! -d "$ETC" ]]; then
|
||||
ETC=$(readlink -f "$HOME/azeroth-server/etc" 2>/dev/null || true)
|
||||
fi
|
||||
if [[ -n "$ETC" && -d "$ETC/modules" ]]; then
|
||||
MP="$ETC/modules/mod_paragon.conf"
|
||||
MPD="$ETC/modules/mod_paragon.conf.dist"
|
||||
if [[ -f "$MP" && -f "$MPD" ]]; then
|
||||
diff -u "$MP" "$MPD" 2>/dev/null | head -80 || true
|
||||
else
|
||||
echo "ETC=$ETC — mod_paragon.conf or .dist missing (MP=$MP MPD=$MPD)"
|
||||
fi
|
||||
else
|
||||
echo "Could not find install etc/modules (set paths manually for diff)."
|
||||
fi
|
||||
|
||||
hr
|
||||
echo "DELIVERABLE for maintainer:"
|
||||
echo "1) Paste 1A–1D (binary mtime, git HEAD, strings, sha256 + revision strings)."
|
||||
echo "2) Paste DATABASE blocks: updates + DBC parity (chrclasses 12, spell_dbc, spellrunecost join)."
|
||||
echo "3) Paste 2A path strings + 2D listings (or MISSING lines)."
|
||||
echo "4) From dev: same 1D sha256 of worldserver OR same SQL block — proves binary/data parity."
|
||||
echo "5) ONE sentence: exact in-game symptom."
|
||||
echo "Done."
|
||||
echo ""
|
||||
echo "Full transcript: $DIAG_OUT"
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
# Omit Docker-only paths from the working tree (native VPS / production clones).
|
||||
# Repository root is the AzerothCore tree (flat layout).
|
||||
#
|
||||
# Run from repository root (directory that contains acore.sh and apps/).
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/vps-sparse-checkout-no-docker.sh
|
||||
#
|
||||
# Restore full tree: git sparse-checkout disable
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
if [[ ! -d .git ]]; then
|
||||
echo "error: run from a git clone (no .git in $ROOT)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git sparse-checkout init --no-cone
|
||||
|
||||
cat >.git/info/sparse-checkout <<'EOF'
|
||||
/*
|
||||
!/docker-compose.yml
|
||||
!/docker-compose.override.yml
|
||||
!/apps/docker/
|
||||
!/env/docker-focal-build/
|
||||
!/.devcontainer/
|
||||
EOF
|
||||
|
||||
if git sparse-checkout reapply 2>/dev/null; then
|
||||
:
|
||||
else
|
||||
git read-tree -mu HEAD
|
||||
fi
|
||||
|
||||
echo "Sparse checkout applied (Docker-only paths omitted)."
|
||||
echo "To restore full tree locally: git sparse-checkout disable"
|
||||
Executable
+181
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fractured / AzerothCore — native VPS rolling update (git + compile).
|
||||
#
|
||||
# Run from anywhere; resolves the repository root from this script's location.
|
||||
# Typical production layout: sources in ~/src/Fractured, install prefix in ~/azeroth-server
|
||||
# (see docs/DEPLOY_LINUX_VPS.md).
|
||||
#
|
||||
# What this does:
|
||||
# 1. git pull on the current branch (optional; can skip)
|
||||
# 2. ./acore.sh compiler build — or compiler all for a full clean rebuild
|
||||
#
|
||||
# Database migrations from data/sql/updates/ run when you next start worldserver/authserver
|
||||
# (Updates.* / SourceDirectory in *.conf). This script does not start or stop daemons unless
|
||||
# you pass --run-after or set FRACTURED_POST_UPDATE_CMD.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/vps-update-server.sh
|
||||
# bash scripts/vps-update-server.sh --full
|
||||
# bash scripts/vps-update-server.sh --no-pull
|
||||
# bash scripts/vps-update-server.sh --dry-run
|
||||
# FRACTURED_POST_UPDATE_CMD='sudo systemctl restart fractured-world' bash scripts/vps-update-server.sh --run-after
|
||||
# bash scripts/vps-update-server.sh --run-after 'sudo systemctl restart fractured-world'
|
||||
#
|
||||
# Environment:
|
||||
# FRACTURED_GIT_REMOTE — remote name (default: origin)
|
||||
# FRACTURED_POST_UPDATE_CMD — shell command run after a successful compile (if --run-after is passed without an argument, this is used)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
NO_PULL=0
|
||||
FULL_BUILD=0
|
||||
COMPILE_ONLY=0
|
||||
DRY_RUN=0
|
||||
DO_RUN_AFTER=0
|
||||
POST_UPDATE_CMD="${FRACTURED_POST_UPDATE_CMD:-}"
|
||||
GIT_REMOTE="${FRACTURED_GIT_REMOTE:-origin}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Fractured VPS update — git pull + compiler (see header in script for full notes).
|
||||
|
||||
Usage:
|
||||
bash scripts/vps-update-server.sh [options]
|
||||
|
||||
Options:
|
||||
--no-pull Skip git pull (only compile current tree).
|
||||
--full ./acore.sh compiler all (clean + configure + compile).
|
||||
--compile-only ./acore.sh compiler compile (incremental).
|
||||
--dry-run Print commands without running them.
|
||||
--run-after [CMD] Run shell command after successful compile. If CMD is omitted,
|
||||
uses FRACTURED_POST_UPDATE_CMD from the environment.
|
||||
|
||||
Environment:
|
||||
FRACTURED_GIT_REMOTE Git remote (default: origin).
|
||||
FRACTURED_POST_UPDATE_CMD Used with bare --run-after.
|
||||
EOF
|
||||
}
|
||||
|
||||
run() {
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
printf '[dry-run] '
|
||||
printf '%q ' "$@"
|
||||
printf '\n'
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--no-pull)
|
||||
NO_PULL=1
|
||||
shift
|
||||
;;
|
||||
--full)
|
||||
FULL_BUILD=1
|
||||
shift
|
||||
;;
|
||||
--compile-only)
|
||||
COMPILE_ONLY=1
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
--run-after)
|
||||
DO_RUN_AFTER=1
|
||||
shift
|
||||
if [[ $# -gt 0 && "$1" != -* ]]; then
|
||||
POST_UPDATE_CMD="$1"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "error: unknown option: $1" >&2
|
||||
echo "Try: bash scripts/vps-update-server.sh --help" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$FULL_BUILD" -eq 1 && "$COMPILE_ONLY" -eq 1 ]]; then
|
||||
echo "error: use only one of --full or --compile-only" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -d "$ROOT/.git" ]]; then
|
||||
echo "error: not a git clone: $ROOT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$ROOT/acore.sh" ]]; then
|
||||
echo "error: acore.sh not found under $ROOT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$ROOT/conf/config.sh" ]]; then
|
||||
echo "error: missing $ROOT/conf/config.sh — copy conf/dist/config.sh and edit (see DEPLOY_LINUX_VPS.md)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$ROOT"
|
||||
|
||||
if [[ "$DO_RUN_AFTER" -eq 1 && -z "${POST_UPDATE_CMD// }" ]]; then
|
||||
echo "error: --run-after needs a command or FRACTURED_POST_UPDATE_CMD set in the environment." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
current_branch() {
|
||||
git symbolic-ref -q --short HEAD || git rev-parse --short HEAD
|
||||
}
|
||||
|
||||
if [[ "$NO_PULL" -eq 0 ]]; then
|
||||
ref="$(current_branch)"
|
||||
if [[ "$ref" == "HEAD" ]]; then
|
||||
echo "error: detached HEAD; checkout a branch or use --no-pull." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "==> git pull $GIT_REMOTE $ref"
|
||||
run git pull "$GIT_REMOTE" "$ref"
|
||||
else
|
||||
echo "==> skipping git pull (--no-pull)"
|
||||
fi
|
||||
|
||||
echo "==> ensuring acore.sh and JSONPath are executable"
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
run chmod +x acore.sh deps/jsonpath/JSONPath.sh
|
||||
else
|
||||
chmod +x acore.sh deps/jsonpath/JSONPath.sh 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [[ "$FULL_BUILD" -eq 1 ]]; then
|
||||
echo "==> ./acore.sh compiler all (clean, configure, compile)"
|
||||
run ./acore.sh compiler all
|
||||
elif [[ "$COMPILE_ONLY" -eq 1 ]]; then
|
||||
echo "==> ./acore.sh compiler compile (incremental; build dir must exist)"
|
||||
run ./acore.sh compiler compile
|
||||
else
|
||||
echo "==> ./acore.sh compiler build (configure + compile)"
|
||||
run ./acore.sh compiler build
|
||||
fi
|
||||
|
||||
if [[ "$DO_RUN_AFTER" -eq 1 ]]; then
|
||||
echo "==> post-update: $POST_UPDATE_CMD"
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
printf '[dry-run] eval %q\n' "$POST_UPDATE_CMD"
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
eval "$POST_UPDATE_CMD"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Done. Restart authserver/worldserver (or your service manager) when ready so new binaries and SQL updates apply."
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# Rebuild acore_world from data/sql/base/db_world only (AzerothCore "shortcut"):
|
||||
# the shipped base `updates` table already records archived migrations as applied,
|
||||
# so worldserver only needs to run pending files under data/sql/updates/db_world
|
||||
# (and your custom/pending paths if used).
|
||||
#
|
||||
# Usage:
|
||||
# export MYSQL_PWD='...'
|
||||
# ./scripts/world-db-from-base.sh -h 10.0.13.252 -u acore -D acore_world
|
||||
#
|
||||
# Requires: mysql client. Optional: pass --force to skip confirmation.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 -h HOST -u USER -D DATABASE [--port PORT] [--force]" >&2
|
||||
echo " Or set: MYSQL_HOST MYSQL_USER MYSQL_DATABASE [MYSQL_PWD in env]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
HOST="${MYSQL_HOST:-}"
|
||||
USER="${MYSQL_USER:-}"
|
||||
DBNAME="${MYSQL_DATABASE:-}"
|
||||
PORT="${MYSQL_PORT:-3306}"
|
||||
FORCE=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h) HOST="$2"; shift 2 ;;
|
||||
-u) USER="$2"; shift 2 ;;
|
||||
-D) DBNAME="$2"; shift 2 ;;
|
||||
--port) PORT="$2"; shift 2 ;;
|
||||
--force) FORCE=1; shift ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
: "${HOST:?set -h or MYSQL_HOST}"
|
||||
: "${USER:?set -u or MYSQL_USER}"
|
||||
: "${DBNAME:?set -D or MYSQL_DATABASE}"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
BASE_DIR="$REPO_ROOT/data/sql/base/db_world"
|
||||
|
||||
if [[ ! -d "$BASE_DIR" ]]; then
|
||||
echo "Base directory not found: $BASE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$FORCE" -ne 1 ]]; then
|
||||
read -r -p "DESTROY database '$DBNAME' on $HOST and rebuild from base? [yes/no]: " ans
|
||||
[[ "$ans" == "yes" ]] || { echo "Aborted."; exit 1; }
|
||||
fi
|
||||
|
||||
MYSQL=(mysql -h "$HOST" -P "$PORT" -u "$USER" --protocol=tcp)
|
||||
"${MYSQL[@]}" -e "DROP DATABASE IF EXISTS \`$DBNAME\`; CREATE DATABASE \`$DBNAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
|
||||
mapfile -t SQL_FILES < <(find "$BASE_DIR" -maxdepth 1 -name '*.sql' -printf '%f\n' | sort)
|
||||
|
||||
for f in "${SQL_FILES[@]}"; do
|
||||
echo ">> $f"
|
||||
"${MYSQL[@]}" "$DBNAME" <"$BASE_DIR/$f"
|
||||
done
|
||||
|
||||
echo ">> Done. Start worldserver once; it should report World DB up-to-date or only apply updates/db_world."
|
||||
@@ -17,10 +17,16 @@
|
||||
|
||||
#include "ARC4.h"
|
||||
#include "Errors.h"
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
Acore::Crypto::ARC4::ARC4() : _ctx(EVP_CIPHER_CTX_new())
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
_cipher = EVP_CIPHER_fetch(nullptr, "RC4", nullptr);
|
||||
#else
|
||||
_cipher = const_cast<EVP_CIPHER*>(EVP_rc4());
|
||||
#endif
|
||||
ASSERT(_cipher);
|
||||
|
||||
EVP_CIPHER_CTX_init(_ctx);
|
||||
int result = EVP_EncryptInit_ex(_ctx, _cipher, nullptr, nullptr, nullptr);
|
||||
@@ -30,7 +36,9 @@ Acore::Crypto::ARC4::ARC4() : _ctx(EVP_CIPHER_CTX_new())
|
||||
Acore::Crypto::ARC4::~ARC4()
|
||||
{
|
||||
EVP_CIPHER_CTX_free(_ctx);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
EVP_CIPHER_free(_cipher);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Acore::Crypto::ARC4::Init(uint8 const* seed, std::size_t len)
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
|
||||
#include "OpenSSLCrypto.h"
|
||||
#include <openssl/crypto.h> // NOTE: this import is NEEDED (even though some IDEs report it as unused)
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
#include <openssl/provider.h>
|
||||
|
||||
OSSL_PROVIDER* LegacyProvider;
|
||||
@@ -53,3 +56,10 @@ void OpenSSLCrypto::threadsCleanup()
|
||||
OSSL_PROVIDER_unload(DefaultProvider);
|
||||
OSSL_PROVIDER_set_default_search_path(nullptr, nullptr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void OpenSSLCrypto::threadsSetup() {}
|
||||
void OpenSSLCrypto::threadsCleanup() {}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -52,7 +52,12 @@ MaxPingTime = 30
|
||||
|
||||
#
|
||||
# RealmServerPort
|
||||
# Description: TCP port to reach the auth server.
|
||||
# Description: TCP port the auth server listens on (login handshake).
|
||||
# 3724 is the stock WoW default; clients with `set realmlist <host>`
|
||||
# (no port) connect here. Production deployments that cannot bind
|
||||
# 3724 (NAT, conflicting service, etc.) can set this to e.g. 47497
|
||||
# and have clients use `set realmlist <host>:47497` -- the
|
||||
# Fractured-patched Wow.exe supports the host:port syntax.
|
||||
# Default: 3724
|
||||
|
||||
RealmServerPort = 3724
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "SQLOperation.h"
|
||||
#include "Transaction.h"
|
||||
#include "WorldDatabase.h"
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <mysqld_error.h>
|
||||
#include <sstream>
|
||||
@@ -59,10 +60,18 @@ DatabaseWorkerPool<T>::DatabaseWorkerPool() :
|
||||
{
|
||||
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
|
||||
|
||||
bool isSupportClientDB = mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION;
|
||||
bool isSameClientDB = mysql_get_client_version() == MYSQL_VERSION_ID;
|
||||
bool isSupportClientDB;
|
||||
bool isSameClientDB;
|
||||
#ifdef MARIADB_VERSION_ID
|
||||
isSupportClientDB = (MARIADB_VERSION_ID >= MIN_MARIADB_CLIENT_VERSION_ID);
|
||||
// MariaDB packages often differ between build host and runtime; strict id match is too brittle.
|
||||
isSameClientDB = true;
|
||||
#else
|
||||
isSupportClientDB = mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION;
|
||||
isSameClientDB = mysql_get_client_version() == MYSQL_VERSION_ID;
|
||||
#endif
|
||||
|
||||
WPFatal(isSupportClientDB, "AzerothCore does not support MySQL versions below 8.0\n\nFound version: {} / {}. Server compiled with: {}.\nSearch the wiki for ACE00043 in Common Errors (https://www.azerothcore.org/wiki/common-errors#ace00043).",
|
||||
WPFatal(isSupportClientDB, "AzerothCore does not support this database client library.\n\nFound version: {} / {}. Server compiled with: {}.\nSearch the wiki for ACE00043 in Common Errors (https://www.azerothcore.org/wiki/common-errors#ace00043).",
|
||||
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
|
||||
WPFatal(isSameClientDB, "Used MySQL library version ({} id {}) does not match the version id used to compile AzerothCore (id {}).\nSearch the wiki for ACE00046 in Common Errors (https://www.azerothcore.org/wiki/common-errors#ace00046).",
|
||||
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
|
||||
@@ -386,32 +395,65 @@ void DatabaseWorkerPool<T>::KeepAlive()
|
||||
*/
|
||||
bool DatabaseIncompatibleVersion(std::string const mysqlVersion)
|
||||
{
|
||||
// anon func to turn a version string into an array of uint8
|
||||
// "1.2.3" => [1, 2, 3]
|
||||
auto parse = [](std::string const& input)
|
||||
auto parseTriplet = [](std::string const& input) -> std::array<unsigned, 3>
|
||||
{
|
||||
std::vector<uint8> result;
|
||||
std::istringstream parser(input);
|
||||
result.push_back(parser.get());
|
||||
for (int i = 1; i < 3; i++)
|
||||
std::array<unsigned, 3> v = { 0, 0, 0 };
|
||||
size_t idx = 0;
|
||||
std::string num;
|
||||
for (char ch : input)
|
||||
{
|
||||
// Skip period
|
||||
parser.get();
|
||||
// Append int from parser to output
|
||||
result.push_back(parser.get());
|
||||
if (ch >= '0' && ch <= '9')
|
||||
num.push_back(ch);
|
||||
else if (ch == '.')
|
||||
{
|
||||
if (!num.empty() && idx < 3)
|
||||
{
|
||||
v[idx++] = static_cast<unsigned>(std::stoul(num));
|
||||
num.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
if (!num.empty() && idx < 3)
|
||||
v[idx] = static_cast<unsigned>(std::stoul(num));
|
||||
return v;
|
||||
};
|
||||
|
||||
// default to values for MySQL
|
||||
uint8 offset = 0;
|
||||
auto compareVersion = [](std::array<unsigned, 3> const& a, std::array<unsigned, 3> const& b) -> int
|
||||
{
|
||||
if (a[0] != b[0])
|
||||
return (a[0] < b[0]) ? -1 : 1;
|
||||
if (a[1] != b[1])
|
||||
return (a[1] < b[1]) ? -1 : 1;
|
||||
if (a[2] != b[2])
|
||||
return (a[2] < b[2]) ? -1 : 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
std::string ver = mysqlVersion;
|
||||
std::string minVersion = MIN_MYSQL_SERVER_VERSION;
|
||||
|
||||
auto parsedMySQLVersion = parse(mysqlVersion.substr(offset));
|
||||
auto parsedMinVersion = parse(minVersion);
|
||||
// MariaDB: version string may be:
|
||||
// - "5.5.5-10.6.11-MariaDB-1:10.6.11+maria~..." (MySQL wire compat prefix)
|
||||
// - "10.6.11-MariaDB-1:10.6.11+maria~..." (no 5.5.5- prefix)
|
||||
// parseTriplet() on the full second form stops at the first '-', yielding 10.6.11.
|
||||
// Never take "firstDash..secondDash" — that becomes "MariaDB" and compares as 0.0.0.
|
||||
if (ver.find("MariaDB") != std::string::npos)
|
||||
{
|
||||
minVersion = MIN_MARIADB_SERVER_VERSION;
|
||||
if (ver.compare(0, 6, "5.5.5-") == 0)
|
||||
{
|
||||
size_t const afterPrefix = 6;
|
||||
size_t const nextDash = ver.find('-', afterPrefix);
|
||||
if (nextDash != std::string::npos)
|
||||
ver = ver.substr(afterPrefix, nextDash - afterPrefix);
|
||||
else
|
||||
ver = ver.substr(afterPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
return std::lexicographical_compare(parsedMySQLVersion.begin(), parsedMySQLVersion.end(),
|
||||
parsedMinVersion.begin(), parsedMinVersion.end());
|
||||
return compareVersion(parseTriplet(ver), parseTriplet(minVersion)) < 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -442,7 +484,7 @@ uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConne
|
||||
}
|
||||
else if (DatabaseIncompatibleVersion(connection->GetServerInfo()))
|
||||
{
|
||||
LOG_ERROR("sql.driver", "AzerothCore does not support MySQL versions below 8.0\n\nFound server version: {}. Server compiled with: {}.",
|
||||
LOG_ERROR("sql.driver", "Database server version is too old.\n\nFound server version: {}. Client library compile id: {}.",
|
||||
connection->GetServerInfo(), MYSQL_VERSION_ID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -32,12 +32,24 @@
|
||||
*/
|
||||
#define MIN_MYSQL_CLIENT_VERSION 80000u
|
||||
|
||||
/**
|
||||
* @def MIN_MARIADB_CLIENT_VERSION_ID
|
||||
* Minimum MariaDB connector MARIADB_VERSION_ID (10.4.0 => 100400)
|
||||
*/
|
||||
#define MIN_MARIADB_CLIENT_VERSION_ID 100400u
|
||||
|
||||
/**
|
||||
* @def MIN_MYSQL_SERVER_VERSION
|
||||
* The minimum MySQL Server Version
|
||||
*/
|
||||
#define MIN_MYSQL_SERVER_VERSION "8.0.0"
|
||||
|
||||
/**
|
||||
* @def MIN_MARIADB_SERVER_VERSION
|
||||
* Minimum MariaDB server (version triplet after the 5.5.5- compatibility prefix)
|
||||
*/
|
||||
#define MIN_MARIADB_SERVER_VERSION "10.4.0"
|
||||
|
||||
template <typename T>
|
||||
class ProducerConsumerQueue;
|
||||
|
||||
|
||||
@@ -129,13 +129,17 @@ uint32 MySQLConnection::Open()
|
||||
|
||||
if (m_connectionInfo.ssl != "")
|
||||
{
|
||||
#ifdef MARIADB_VERSION_ID
|
||||
my_bool const ssl_enforce = (m_connectionInfo.ssl == "ssl") ? 1 : 0;
|
||||
if (ssl_enforce)
|
||||
mysql_options(mysqlInit, MYSQL_OPT_SSL_ENFORCE, &ssl_enforce);
|
||||
#else
|
||||
mysql_ssl_mode opt_use_ssl = SSL_MODE_DISABLED;
|
||||
if (m_connectionInfo.ssl == "ssl")
|
||||
{
|
||||
opt_use_ssl = SSL_MODE_REQUIRED;
|
||||
}
|
||||
|
||||
mysql_options(mysqlInit, MYSQL_OPT_SSL_MODE, (char const*)&opt_use_ssl);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_Mysql = reinterpret_cast<MySQLHandle*>(mysql_real_connect(mysqlInit, m_connectionInfo.host.c_str(), m_connectionInfo.user.c_str(),
|
||||
@@ -216,7 +220,7 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt)
|
||||
|
||||
uint32 _s = getMSTime();
|
||||
|
||||
#if MYSQL_VERSION_ID >= 80300
|
||||
#if MYSQL_VERSION_ID >= 80300 && !defined(MARIADB_VERSION_ID)
|
||||
if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
|
||||
#else
|
||||
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
|
||||
@@ -268,7 +272,7 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement
|
||||
|
||||
uint32 _s = getMSTime();
|
||||
|
||||
#if MYSQL_VERSION_ID >= 80300
|
||||
#if MYSQL_VERSION_ID >= 80300 && !defined(MARIADB_VERSION_ID)
|
||||
if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
|
||||
#else
|
||||
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
|
||||
|
||||
@@ -669,7 +669,12 @@ bool AuctionHouseUsablePlayerInfo::PlayerCanUseItem(ItemTemplate const* proto) c
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((proto->AllowableClass & classMask) == 0 || (proto->AllowableRace & raceMask) == 0)
|
||||
// mod-paragon: class 12 (Paragon) ignores AllowableClass for AH "Usable"
|
||||
// filter. classMask here is the searching player's mask; PARAGON_BIT 0x800
|
||||
// = (1 << (12 - 1)). Race restriction still applies.
|
||||
bool const searcherIsParagon = (classMask & 0x800u) != 0;
|
||||
if ((!searcherIsParagon && (proto->AllowableClass & classMask) == 0)
|
||||
|| (proto->AllowableRace & raceMask) == 0)
|
||||
return false;
|
||||
|
||||
if (proto->RequiredSkill != 0)
|
||||
|
||||
@@ -208,6 +208,46 @@ namespace Trainer
|
||||
|
||||
bool Trainer::IsTrainerValidForPlayer(Player const* player) const
|
||||
{
|
||||
// Paragon (class 12) learns class abilities exclusively through the
|
||||
// Character Advancement panel (mod-paragon). Generic class trainers
|
||||
// refuse interaction. Pet trainers, mount/profession trainers, and
|
||||
// specialized portal/teleport trainers (mage portal NPCs) stay valid:
|
||||
// - Pet trainers: pet-skill purchases for hunter pets aren't covered
|
||||
// by the panel and should remain trainer-driven.
|
||||
// - Portal/teleport trainers: identified at runtime as a Class-type
|
||||
// trainer whose spells are ALL TELEPORT_UNITS or TRANS_DOOR
|
||||
// effects. The big general mage trainer fails this check (it
|
||||
// teaches Fireball, Frostbolt, etc.) and is correctly blocked.
|
||||
if (player && player->getClass() == CLASS_PARAGON
|
||||
&& GetTrainerType() == Type::Class
|
||||
&& !_spells.empty())
|
||||
{
|
||||
bool onlyPortalsAndTeleports = true;
|
||||
for (Spell const& s : _spells)
|
||||
{
|
||||
SpellInfo const* info = sSpellMgr->GetSpellInfo(s.SpellId);
|
||||
if (!info)
|
||||
continue;
|
||||
bool isPortalOrTeleport = false;
|
||||
for (SpellEffectInfo const& eff : info->GetEffects())
|
||||
{
|
||||
if (eff.Effect == SPELL_EFFECT_TELEPORT_UNITS
|
||||
|| eff.Effect == SPELL_EFFECT_TRANS_DOOR)
|
||||
{
|
||||
isPortalOrTeleport = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isPortalOrTeleport)
|
||||
{
|
||||
onlyPortalsAndTeleports = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!onlyPortalsAndTeleports)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetTrainerRequirement())
|
||||
return true;
|
||||
|
||||
|
||||
@@ -10687,7 +10687,12 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(pProto->AllowableClass & getClassMask()) && pProto->Bonding == BIND_WHEN_PICKED_UP && !IsGameMaster())
|
||||
// mod-paragon: class 12 ignores BoP buy-side AllowableClass gate, so
|
||||
// class-restricted vendor items (e.g. class glyphs) can be purchased.
|
||||
if (getClass() != CLASS_PARAGON
|
||||
&& !(pProto->AllowableClass & getClassMask())
|
||||
&& pProto->Bonding == BIND_WHEN_PICKED_UP
|
||||
&& !IsGameMaster())
|
||||
{
|
||||
SendBuyError(BUY_ERR_CANT_FIND_ITEM, nullptr, item, 0);
|
||||
return false;
|
||||
@@ -14052,7 +14057,10 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank, bool command /*= fa
|
||||
return;
|
||||
|
||||
// xinef: prevent learn talent for different class (cheating)
|
||||
if ((getClassMask() & talentTabInfo->ClassMask) == 0)
|
||||
// mod-paragon: Paragon (class 12) can spec into any class's talent tree
|
||||
// via the Character Advancement panel; bypass the class-mask check.
|
||||
if (getClass() != CLASS_PARAGON
|
||||
&& (getClassMask() & talentTabInfo->ClassMask) == 0)
|
||||
return;
|
||||
|
||||
// xinef: find current talent rank
|
||||
@@ -14081,7 +14089,12 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank, bool command /*= fa
|
||||
}
|
||||
|
||||
// xinef: check if talent deponds on another talent
|
||||
if (talentInfo->DependsOn > 0)
|
||||
// mod-paragon: Character Advancement gates talents by AE/TE essence cost,
|
||||
// not by the column-arrow prereq from Blizzard's spec UI. For class 12
|
||||
// (Paragon) we skip the DependsOn check so e.g. Deep Wounds, Bloody
|
||||
// Vengeance and Expose Weakness can be picked without first speccing into
|
||||
// their unrelated prereq sibling.
|
||||
if (talentInfo->DependsOn > 0 && getClass() != CLASS_PARAGON)
|
||||
if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn))
|
||||
{
|
||||
bool hasEnoughRank = false;
|
||||
|
||||
@@ -2364,7 +2364,16 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const
|
||||
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
||||
}
|
||||
|
||||
if ((proto->AllowableClass & getClassMask()) == 0 || (proto->AllowableRace & getRaceMask()) == 0)
|
||||
// mod-paragon: class 12 (Paragon) ignores AllowableClass entirely, so any
|
||||
// class-restricted item (including class glyphs) can be equipped/used.
|
||||
// Race restriction still applies; proficiency/level/skill checks below
|
||||
// still gate it sensibly via the standard skill cascade.
|
||||
if (getClass() != CLASS_PARAGON
|
||||
&& (proto->AllowableClass & getClassMask()) == 0)
|
||||
{
|
||||
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
||||
}
|
||||
if ((proto->AllowableRace & getRaceMask()) == 0)
|
||||
{
|
||||
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
||||
}
|
||||
@@ -2430,7 +2439,11 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje
|
||||
SKILL_FISHING
|
||||
}; //Copy from function Item::GetSkill()
|
||||
|
||||
if ((proto->AllowableClass & getClassMask()) == 0 || (proto->AllowableRace & getRaceMask()) == 0)
|
||||
// mod-paragon: class 12 ignores AllowableClass for LFG roll eligibility.
|
||||
if (getClass() != CLASS_PARAGON
|
||||
&& (proto->AllowableClass & getClassMask()) == 0)
|
||||
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
||||
if ((proto->AllowableRace & getRaceMask()) == 0)
|
||||
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
||||
|
||||
if (proto->RequiredSpell != 0 && !HasSpell(proto->RequiredSpell))
|
||||
|
||||
@@ -385,6 +385,13 @@ void Player::UpdateAttackPowerAndDamage(bool ranged)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (getClass() == CLASS_PARAGON)
|
||||
{
|
||||
// Fractured class 12: same hybrid curve as requested for Paragon UI
|
||||
// (level*2 + AGI + STR - 20). Implemented in core so we do not rely
|
||||
// on PlayerScript hooks in this hot path.
|
||||
val2 = level * 2.0f + GetStat(STAT_AGILITY) + GetStat(STAT_STRENGTH) - 20.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
val2 = GetStat(STAT_AGILITY) - 10.0f;
|
||||
@@ -481,6 +488,10 @@ void Player::UpdateAttackPowerAndDamage(bool ranged)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (getClass() == CLASS_PARAGON)
|
||||
{
|
||||
val2 = level * 2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f;
|
||||
}
|
||||
else if (IsClass(CLASS_MAGE, CLASS_CONTEXT_STATS) || IsClass(CLASS_PRIEST, CLASS_CONTEXT_STATS) || IsClass(CLASS_WARLOCK, CLASS_CONTEXT_STATS))
|
||||
{
|
||||
val2 = GetStat(STAT_STRENGTH) - 10.0f;
|
||||
|
||||
@@ -9046,6 +9046,21 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask)
|
||||
DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus();
|
||||
DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellDamageBonus();
|
||||
|
||||
// Fractured class 12 (Paragon) intrinsic spell power:
|
||||
// SP = level*2 + INT + SPI - 20 (clamped at 0)
|
||||
// Read live from current stats so character-sheet refreshes (via
|
||||
// UpdateSpellDamageAndHealingBonus) and live spell casts both see the
|
||||
// up-to-date value with no script hooks or m_baseSpellPower mutation.
|
||||
if (ToPlayer()->getClass() == CLASS_PARAGON)
|
||||
{
|
||||
int32 paragonSP = int32(GetLevel()) * 2
|
||||
+ int32(GetStat(STAT_INTELLECT))
|
||||
+ int32(GetStat(STAT_SPIRIT))
|
||||
- 20;
|
||||
if (paragonSP > 0)
|
||||
DoneAdvertisedBenefit += paragonSP;
|
||||
}
|
||||
|
||||
// Damage bonus from stats
|
||||
AuraEffectList const& mDamageDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT);
|
||||
for (AuraEffectList::const_iterator i = mDamageDoneOfStatPercent.begin(); i != mDamageDoneOfStatPercent.end(); ++i)
|
||||
@@ -9803,6 +9818,20 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask)
|
||||
AdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus();
|
||||
AdvertisedBenefit += ToPlayer()->GetBaseSpellHealingBonus();
|
||||
|
||||
// Fractured class 12 (Paragon) intrinsic spell power: same level*2 +
|
||||
// INT + SPI - 20 floor as on the damage side (the character sheet
|
||||
// shows a single Spell Power value, so both sides must add the same
|
||||
// bonus).
|
||||
if (ToPlayer()->getClass() == CLASS_PARAGON)
|
||||
{
|
||||
int32 paragonSP = int32(GetLevel()) * 2
|
||||
+ int32(GetStat(STAT_INTELLECT))
|
||||
+ int32(GetStat(STAT_SPIRIT))
|
||||
- 20;
|
||||
if (paragonSP > 0)
|
||||
AdvertisedBenefit += paragonSP;
|
||||
}
|
||||
|
||||
// Healing bonus from stats
|
||||
AuraEffectList const& mHealingDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT);
|
||||
for (AuraEffectList::const_iterator i = mHealingDoneOfStatPercent.begin(); i != mHealingDoneOfStatPercent.end(); ++i)
|
||||
|
||||
@@ -908,7 +908,12 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry)
|
||||
{
|
||||
if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(item->item))
|
||||
{
|
||||
if (!(itemTemplate->AllowableClass & _player->getClassMask()) && itemTemplate->Bonding == BIND_WHEN_PICKED_UP && !_player->IsGameMaster())
|
||||
// mod-paragon: class 12 sees every BoP class-restricted item
|
||||
// in vendor lists (class glyphs, class tier sets, ...).
|
||||
if (_player->getClass() != CLASS_PARAGON
|
||||
&& !(itemTemplate->AllowableClass & _player->getClassMask())
|
||||
&& itemTemplate->Bonding == BIND_WHEN_PICKED_UP
|
||||
&& !_player->IsGameMaster())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -7296,8 +7296,16 @@ SpellCastResult Spell::CheckItems(uint32* param1, uint32* param2)
|
||||
{
|
||||
// Xinef: this is not true in my opinion, in eg bladestorm will not be canceled after disarm
|
||||
//if (!HasTriggeredCastFlag(TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT))
|
||||
if (m_caster->IsPlayer() && !m_caster->ToPlayer()->HasItemFitToSpellRequirements(m_spellInfo))
|
||||
return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
|
||||
if (m_caster->IsPlayer())
|
||||
{
|
||||
// Cast-from-glyph: many glyph on-use spells set EquippedItemClass to ITEM_CLASS_GLYPH.
|
||||
// HasItemFitToSpellRequirements only implements weapon/armor, so it would always fail here
|
||||
// even though the glyph item in the bag is the valid spell source.
|
||||
bool const castFromGlyphScroll = m_CastItem && m_CastItem->GetTemplate() &&
|
||||
m_CastItem->GetTemplate()->Class == ITEM_CLASS_GLYPH;
|
||||
if (!castFromGlyphScroll && !m_caster->ToPlayer()->HasItemFitToSpellRequirements(m_spellInfo))
|
||||
return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
|
||||
}
|
||||
}
|
||||
|
||||
// do not take reagents for these item casts
|
||||
|
||||
@@ -5368,6 +5368,56 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
LockEntry* key = const_cast<LockEntry*>(sLockStore.LookupEntry(36)); // 3366 Opening, allows to open without proper key
|
||||
key->Type[2] = LOCK_KEY_NONE;
|
||||
|
||||
// Fractured / Paragon: DK weapon-line "passives" Forceful Deflection and
|
||||
// Runic Focus ship in 3.3.5a Spell.dbc without SPELL_ATTR0_PASSIVE set.
|
||||
// SpellInfo::IsPassive() is therefore false, and mod-paragon's panel-learn
|
||||
// diff treats them as castable actives and revokes them — while true
|
||||
// actives (Blood Presence, Death Coil, Death Grip, ...) must stay
|
||||
// stripped. Mark these two passive in-memory so the panel policy matches
|
||||
// the spellbook UX for every class (stock DK benefits too).
|
||||
ApplySpellFix({ 49410, 61455 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->Attributes |= SPELL_ATTR0_PASSIVE;
|
||||
});
|
||||
|
||||
// Fractured: strip reagent requirements from every player-class spell at
|
||||
// load time. Filtered by SpellFamilyName != 0 so that profession spells
|
||||
// (cooking, alchemy, enchanting, blacksmithing, jewelcrafting, leatherworking,
|
||||
// tailoring, engineering, inscription, mining, herbalism, skinning, fishing,
|
||||
// first aid — all SpellFamilyName == SPELLFAMILY_GENERIC == 0) keep their
|
||||
// mats and only the class abilities that asked for ankhs / candles / soul
|
||||
// shards / verdant spheres / etc. cast freely. Done here in core spell
|
||||
// data rather than as a runtime bypass in Spell::CheckItems / TakeReagents
|
||||
// so the change is data-driven (the in-memory SpellInfo simply has no
|
||||
// reagents to require). The client-side preflight is mirrored by the
|
||||
// matching Spell.dbc patch shipped via patch-enUS-4.MPQ
|
||||
// (fractured-tooling/_patch_spell_dbc_reagents.py).
|
||||
{
|
||||
uint32 fixedClassSpells = 0;
|
||||
for (uint32 spellId = 1; spellId < sSpellMgr->GetSpellInfoStoreSize(); ++spellId)
|
||||
{
|
||||
SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!info || info->SpellFamilyName == 0)
|
||||
continue;
|
||||
|
||||
bool hadAny = false;
|
||||
for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i)
|
||||
if (info->Reagent[i] != 0 || info->ReagentCount[i] != 0)
|
||||
{ hadAny = true; break; }
|
||||
if (!hadAny)
|
||||
continue;
|
||||
|
||||
SpellInfo* mut = const_cast<SpellInfo*>(info);
|
||||
for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i)
|
||||
{
|
||||
mut->Reagent[i] = 0;
|
||||
mut->ReagentCount[i] = 0;
|
||||
}
|
||||
++fixedClassSpells;
|
||||
}
|
||||
LOG_INFO("server.loading", ">> Fractured: cleared reagents on {} class spells", fixedClassSpells);
|
||||
}
|
||||
|
||||
LOG_INFO("server.loading", ">> Loading spell dbc data corrections in {} ms", GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// This is where scripts' loading functions should be declared:
|
||||
// void MyExampleScript()
|
||||
|
||||
// The name of this function should match:
|
||||
// void Add${NameOfDirectory}Scripts()
|
||||
void AddCustomScripts()
|
||||
{
|
||||
// MyExampleScript()
|
||||
}
|
||||
@@ -215,6 +215,14 @@ Updates.AllowRehash = 1
|
||||
# -1 - (Enabled - unlimited)
|
||||
|
||||
Updates.CleanDeadRefMaxCount = 3
|
||||
|
||||
#
|
||||
# Updates.ExceptionShutdownDelay
|
||||
# Description: Time (in milliseconds) to wait before shutting down after a fatal exception (e.g. failed SQL update).
|
||||
# Default: 10000 - 10 seconds
|
||||
# 0 - Disabled (immediate shutdown)
|
||||
|
||||
Updates.ExceptionShutdownDelay = 10000
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
dist/
|
||||
launcher.json
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,169 @@
|
||||
# Fractured Launcher (Electron)
|
||||
|
||||
**Windows** and **Linux (AppImage)** launcher with **no extra console window**, **native Browse folder** dialog, **Gitea or GitHub** release assets + GitHub repo file sync, **realmlist**, optional **auth**, **Play**, and **auto-update** (via `electron-updater`). This is the **only** supported client launcher in this repo.
|
||||
|
||||
## Requirements
|
||||
|
||||
- [Node.js](https://nodejs.org/) 20+ (includes npm)
|
||||
|
||||
## Run from source
|
||||
|
||||
```bash
|
||||
cd tools/fractured-launcher-electron
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
On first run, `launcher.json` is created next to the app (dev: in this folder).
|
||||
|
||||
### Where patches download from
|
||||
|
||||
- **Recommended (self-hosted Gitea):** set **`gitea.base_url`**, **`gitea.owner`**, **`gitea.repo`** in `launcher.json` (see **`default-launcher.json`**). Players need **`GITEA_TOKEN`** (or the env name in **`gitea.token_env`**) if the Gitea repo is **private** — same trade-off as any private host (per-player token, SSO proxy, or a read-only deploy token you accept distributing).
|
||||
- **Fallback:** if **`gitea.base_url`** is empty, **`from_release`** uses the **GitHub** Releases API against **`github.owner` / `github.repo`** (defaults to this **`Fractured`** repo for non-release paths), with optional **`GITHUB_TOKEN`** for private assets.
|
||||
|
||||
## Build Windows installers
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run pack:win
|
||||
```
|
||||
|
||||
Produces under **`dist/`**:
|
||||
|
||||
| Artifact | Purpose |
|
||||
|----------|---------|
|
||||
| `Fractured-Launcher-${version}-Setup.exe` (NSIS) | **Recommended for players** — supports seamless **auto-update** and restart. |
|
||||
| `Fractured-Launcher-${version}-Windows-Portable.exe` | No installer; players replace the file manually. Auto-update is **less reliable** than NSIS. |
|
||||
|
||||
## Build Linux AppImage
|
||||
|
||||
```bash
|
||||
cd tools/fractured-launcher-electron
|
||||
npm install
|
||||
npm run pack:linux
|
||||
```
|
||||
|
||||
Produces **`dist/Fractured-Launcher-${version}-Linux-x86_64.AppImage`**. Same **`lib/baked-gitea-channel.js`** and **`default-launcher.json`** as Windows; run on **Linux** (or use **Fractured launcher CI** / **Sync release to Gitea**, which upload this file to Gitea with the Windows installers).
|
||||
|
||||
**Quick local test (avoids tag snapshot / CI):**
|
||||
- **Linux:** from repo root, **`bash tools/fractured-launcher-electron/scripts/manual-pack-linux.sh`** → **`dist/*.AppImage`**.
|
||||
- **Windows:** on a Windows machine, **`cd tools/fractured-launcher-electron`**, **`npm ci`**, **`npm run pack:win`** → **`dist/*.exe`**.
|
||||
|
||||
### Hardcoded Gitea channel (non-token)
|
||||
|
||||
**`lib/baked-gitea-channel.js`** exports **`base_url`**, **`owner`**, **`repo`**, **`release_tag`**. Set those strings once in the repo (same values you use for CI upload — not secret). At runtime **`config-store`** merges them into **`gitea.*`** so **`launcher.json`** does not need those fields; **`GITEA_TOKEN`** (or **`gitea.token_env`**) is still only for **private** Gitea. Leave a field **`''`** in the baked file to fall back to **`default-launcher.json`** / user **`launcher.json`** for that key.
|
||||
|
||||
**`npm run pack:win`** is plain **electron-builder** — no inject step, no extra JSON beside the app.
|
||||
|
||||
## Auto-update behaviour
|
||||
|
||||
- **Packaged** builds only (`npm run pack:win` output). In `npm start` dev mode, update checks are skipped (button still explains that).
|
||||
- **No implicit GitHub feed:** the app does **not** guess `package.json` → `repository` anymore. Without configuration you get a clear “skipped” message instead of a **404** on a private repo.
|
||||
- **Configured feeds** (first match wins): **`update_feed_url` / `LAUNCHER_UPDATE_URL`** (generic `latest.yml`); or **`gitea`** block filled in + **`GITEA_TOKEN`** when the instance is private (resolves `…/releases/download/{tag}/`); or **`GITHUB_TOKEN`** + **`github.owner` / `github.repo`** for **private** GitHub releases only.
|
||||
- **~5 seconds** after launch, then **every 6 hours**, the app checks when a feed is configured.
|
||||
- When a download finishes, a dialog offers **Restart now** (calls `quitAndInstall`) or **Later**.
|
||||
- **Manual check:** button **Check launcher updates** in the UI.
|
||||
|
||||
### Where launcher updates are hosted
|
||||
|
||||
**`npm run publish:win`** runs **`electron-builder` with `--publish never`** — artifacts stay in **`dist/`**; CI uploads them to Gitea when you **publish a GitHub release**. For ad-hoc uploads, use **`scripts/upload-release-to-gitea.sh`**. For launcher auto-update, prefer:
|
||||
|
||||
- Set **`update_feed_url`** (or **`LAUNCHER_UPDATE_URL`**) to a **generic** HTTPS base URL where **`latest.yml`** and the installer files are hosted (often the same Gitea release attachment URLs pattern your reverse proxy exposes), **or**
|
||||
- Keep publishing to a GitHub release only for **`latest.yml`** + installers if you accept that small metadata/binary channel there.
|
||||
|
||||
**Private GitHub** updater: set **`GH_TOKEN`** / **`GITHUB_TOKEN`** / **`github.token_env`** as documented in `lib/auto-update.js` behaviour.
|
||||
|
||||
**Generic feed:** optional Bearer token via the same token envs if your static host checks `Authorization`.
|
||||
|
||||
### Publishing a new launcher version
|
||||
|
||||
1. Bump **`version`** in `package.json` on `main` (or your release branch) and merge.
|
||||
2. Create a **GitHub release** (tag + attach patches / `Wow.exe` if needed) and click **Publish** — **Sync release to Gitea** builds **Windows + Linux** launcher artifacts and mirrors everything to Gitea.
|
||||
3. Local check: **`npm run pack:win`** (on Windows) or **`npm run pack:linux`** / **`scripts/manual-pack-linux.sh`**, then **`scripts/upload-release-to-gitea.sh`** with the same **`GITEA_*`** env vars as CI if you need a manual upload.
|
||||
|
||||
## Sync to Gitea (patches + launcher binaries)
|
||||
|
||||
CI workflow **Sync release to Gitea** (`.github/workflows/gitea-release-sync.yml`) runs on **every published GitHub release** on this repo:
|
||||
|
||||
1. Triggers on **release published** on **`Dawnforger/Fractured`** (or **workflow_dispatch** with a tag).
|
||||
2. Builds **Windows** (NSIS + portable) and **Linux** (AppImage) in parallel, each using **`tools/fractured-launcher-electron` from the default branch** (overlaid onto the tag checkout), so older release tags never ship a launcher missing new **`lib/*.js`** files.
|
||||
3. Downloads **all assets** attached to that **GitHub** release (MPQs, patched `Wow.exe`, etc.).
|
||||
4. Merges with the built launcher artifacts and uploads everything to a **Gitea release** with the **same tag** (existing attachments on that Gitea release are replaced).
|
||||
|
||||
**GitHub Actions secrets** (repository → Settings → Secrets and variables → Actions):
|
||||
|
||||
| Secret | Example |
|
||||
|--------|---------|
|
||||
| **`GITEA_BASE_URL`** | `https://git.yourdomain.com` (no trailing slash) |
|
||||
| **`GITEA_TOKEN`** | Gitea personal access token with permission to manage releases and attachments on the target repo |
|
||||
| **`GITEA_OWNER`** | Organization or username on Gitea |
|
||||
| **`GITEA_REPO`** | Repository name — must already have **at least one commit** (Gitea returns HTTP 422 “repo is empty” for zero-commit repos; push e.g. a README on **`main`** or set **`GITEA_TARGET_REF`** to your default branch) |
|
||||
|
||||
**Optional variable** (Settings → Variables): **`GITEA_TARGET_REF`** — default branch/commitish used **only when the workflow must create a new Gitea release** and Gitea needs `target_commitish` (defaults to **`main`** in the upload script if unset).
|
||||
|
||||
**Player `launcher.json`:** packaged builds should already include **`gitea.base_url` / `owner` / `repo`** from the bake step above. Players only need to set **`GITEA_TOKEN`** (or your **`token_env`**) if the Gitea repo is **private**. To point at another instance, edit **`gitea`** in **`launcher.json`**:
|
||||
|
||||
```json
|
||||
"gitea": {
|
||||
"base_url": "https://git.yourdomain.com",
|
||||
"owner": "myorg",
|
||||
"repo": "fractured-patches",
|
||||
"release_tag": "latest",
|
||||
"token_env": "GITEA_TOKEN"
|
||||
}
|
||||
```
|
||||
|
||||
**Manual upload:** `bash scripts/upload-release-to-gitea.sh /path/to/files v1.0.0` with the same env vars as CI.
|
||||
|
||||
### Sync did not run / Gitea unchanged — checklist
|
||||
|
||||
1. **Git tag ≠ GitHub Release** — Only **Releases** (published on the GitHub **Releases** page) trigger this workflow. If your teammate only **`git push --tags`**, create a **Release** from that tag and click **Publish** (or run **Actions → Sync release to Gitea → Run workflow** and enter the tag).
|
||||
2. **Draft release** — Must click **Publish release**; drafts do not mirror.
|
||||
3. **Workflow on default branch** — GitHub runs `release` workflows from the **default branch** (e.g. `main`). Ensure `.github/workflows/gitea-release-sync.yml` is merged there.
|
||||
4. **Repo name guard** — Jobs use `if: github.repository == 'Dawnforger/Fractured'`. Forks or renames must change that line or runs are skipped.
|
||||
5. **Secrets** — **`GITEA_BASE_URL`**, **`GITEA_TOKEN`**, **`GITEA_OWNER`**, **`GITEA_REPO`** must be set under **Settings → Secrets and variables → Actions**. A failed “Upload to Gitea” step usually prints which is missing.
|
||||
6. **Actions tab** — Open the latest **Sync release to Gitea** run; a red **build-electron** (old tag without `package-lock.json`, etc.) or **Upload to Gitea** step shows the real error.
|
||||
7. **HTTP 422 `repo is empty`** — The Gitea repo has **no commits** yet. Push any initial commit (e.g. **Add README** in the Gitea web UI, or `git push` to **`main`**). Optionally set **`GITEA_TARGET_REF`** to match your real default branch if it is not **`main`**. From this repo you can run **`scripts/bootstrap-gitea-repo.sh`** (see script header for `GITEA_*` env or pass the HTTPS/SSH clone URL as the first argument).
|
||||
|
||||
### Private Gitea token for players
|
||||
|
||||
Do **not** embed a shared admin PAT in a shipped `launcher.json`. Prefer read-only tokens scoped to one repo, short-lived tokens, or a small auth service that redirects to signed URLs.
|
||||
|
||||
**Release asset names** must match **`files[].source`** when **`from_release`**: true. Use **`release_tag`**: `"latest"` or a pinned tag matching both GitHub and Gitea.
|
||||
|
||||
## Patch versions (same filenames, different bytes)
|
||||
|
||||
The launcher does **not** read Git commits. For **turn-key** updates when asset names stay fixed (`patch-Z.MPQ`, `Wow-patched.exe`, …):
|
||||
|
||||
1. Ship **`patch-manifest.json`** next to those files on **every** release (Gitea/GitHub attachment). It lists a **`version`** label (any string you bump per release, e.g. `v0.9.0-client`) and a **`sha256`** per **`files[].source`** name.
|
||||
2. With **`patch_manifest.enabled`**: true (default in **`default-launcher.json`**), **Download updates** first fetches the manifest from the same release channel. If the files already on disk match those checksums, the player sees **“already match build … (nothing to download)”** — no redundant downloads.
|
||||
3. After a real download, the launcher **re-hashes** installed files and compares to the manifest; mismatch → clear error. It also writes **`.fractured/patch-state.json`** under the WoW folder so the UI can show **“Installed client files: …”**.
|
||||
|
||||
If **`patch-manifest.json`** is missing on a release, the launcher falls back to **always downloading** all configured files (same as before).
|
||||
|
||||
**Generate the manifest** when you cut a release (paths are your local patch binaries):
|
||||
|
||||
```bash
|
||||
cd /path/to/staging
|
||||
node tools/fractured-launcher-electron/scripts/generate-patch-manifest.js v0.9.0-client patch-Z.MPQ Wow-patched.exe > patch-manifest.json
|
||||
```
|
||||
|
||||
Attach **`patch-manifest.json`** together with the MPQ/exe to the GitHub release (CI sync copies it to Gitea with everything else).
|
||||
|
||||
## CI
|
||||
|
||||
Workflow **Fractured launcher CI** (`.github/workflows/fractured-launcher-ci.yml`) runs on pushes/PRs under `tools/fractured-launcher-electron/`: **Windows** (`npm run pack:win`) and **Linux** (`npm run pack:linux`) jobs, each **`electron-builder … --publish never`**. **Actions → Fractured launcher CI → Run workflow** runs it manually.
|
||||
|
||||
**Sync release to Gitea** (`.github/workflows/gitea-release-sync.yml`) uses the same pack commands. If you see `GH_TOKEN` / `GitHubPublisher` errors in logs, the job is almost certainly an old **Re-run failed jobs** — open **Actions → Sync release to Gitea → Run workflow**, enter the tag, and start a **new** run instead.
|
||||
|
||||
## Config
|
||||
|
||||
Schema is defined by **`default-launcher.json`** (shipped in the app; first run copies to `launcher.json` beside the executable):
|
||||
|
||||
- **`game_dir`**: WoW 3.3.5a root (contains `Wow.exe`).
|
||||
- **`update_feed_url`**: optional generic HTTPS base for launcher auto-update.
|
||||
- **`launcher_updates_from_github`**: default **`false`**. Only when **`true`** will a **`GITHUB_TOKEN`** (or **`github.token_env`**) enable **electron-updater**’s GitHub provider against **`github.owner` / `github.repo`**. Leave **`false`** when launcher binaries and **`latest.yml`** live on **Gitea** (use **`gitea`** + token instead) so a stray GitHub token does not produce “No published versions on GitHub”.
|
||||
- **`gitea`**: **`base_url`**, **`owner`**, **`repo`**, **`release_tag`**, **`token_env`** — when **`base_url`** is set (and owner/repo set), **`from_release`** downloads and (with token if needed) the **generic** updater feed use **Gitea**. **Required** for players if your CI mirrors patches/launchers to Gitea only.
|
||||
- **`github`**: used for **non-release** repo paths (`from_release`: false) and for **GitHub** **`from_release`** when **`gitea.base_url`** is empty.
|
||||
- **`patch_manifest`**: **`enabled`**, **`source`** (default `patch-manifest.json`), **`from_release`** — checksum-based skip + verify (see above).
|
||||
- **`files`**, **`realmlist`**, **`auth`**, **`launch`**.
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"game_dir": "",
|
||||
"update_feed_url": "",
|
||||
"launcher_updates_from_github": false,
|
||||
"gitea": {
|
||||
"base_url": "",
|
||||
"owner": "",
|
||||
"repo": "",
|
||||
"release_tag": "latest",
|
||||
"token_env": "GITEA_TOKEN"
|
||||
},
|
||||
"github": {
|
||||
"owner": "Dawnforger",
|
||||
"repo": "Fractured",
|
||||
"ref": "main",
|
||||
"release_tag": "latest",
|
||||
"token_env": "GITHUB_TOKEN"
|
||||
},
|
||||
"patch_manifest": {
|
||||
"enabled": true,
|
||||
"source": "patch-manifest.json",
|
||||
"from_release": true
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"source": "patch-Z.MPQ",
|
||||
"dest": "Data/patch-Z.MPQ",
|
||||
"backup": true,
|
||||
"from_release": true
|
||||
},
|
||||
{
|
||||
"source": "Wow-patched.exe",
|
||||
"dest": "Wow.exe",
|
||||
"backup": true,
|
||||
"from_release": true
|
||||
}
|
||||
],
|
||||
"realmlist": {
|
||||
"enabled": true,
|
||||
"line": "set realmlist fracturedwow.ddns.net:47497",
|
||||
"paths": ["Data/enUS/realmlist.wtf", "Data/enGB/realmlist.wtf"]
|
||||
},
|
||||
"auth": {
|
||||
"enabled": false,
|
||||
"url": "https://auth.your-realm.example/api/launcher/login",
|
||||
"method": "POST",
|
||||
"username_field": "username",
|
||||
"password_field": "password"
|
||||
},
|
||||
"launch": {
|
||||
"exe": "Wow.exe",
|
||||
"args": [],
|
||||
"linux_wrapper": ["wine"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" />
|
||||
<title>Fractured Launcher</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Fractured Launcher</h1>
|
||||
<p class="sub">Point at your 3.3.5a client, download patches, then play.</p>
|
||||
</header>
|
||||
|
||||
<section class="card">
|
||||
<label class="lbl">World of Warcraft folder (contains <span id="wowExeName">Wow.exe</span>)</label>
|
||||
<div class="row">
|
||||
<input type="text" id="gameDir" placeholder="Browse… or paste the folder that contains Wow.exe" />
|
||||
<button type="button" id="btnBrowse">Browse…</button>
|
||||
<button type="button" id="btnSaveFolder" class="primary">Save folder</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card hidden" id="authCard">
|
||||
<label class="lbl">Account</label>
|
||||
<div class="row stack">
|
||||
<input type="text" id="username" autocomplete="username" placeholder="Username" />
|
||||
<input type="password" id="password" autocomplete="current-password" placeholder="Password" />
|
||||
<button type="button" id="btnAuth" class="primary">Sign in</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card row-actions">
|
||||
<button type="button" id="btnCheckLauncher" class="ghost">Check launcher updates</button>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<button type="button" id="btnSync" class="primary wide" disabled>Download updates</button>
|
||||
<button type="button" id="btnPlay" class="success wide hidden" disabled>Play</button>
|
||||
</section>
|
||||
|
||||
<pre id="log" class="log" aria-live="polite"></pre>
|
||||
|
||||
<script src="renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,148 @@
|
||||
'use strict';
|
||||
|
||||
const { dialog } = require('electron');
|
||||
const { autoUpdater } = require('electron-updater');
|
||||
const { useGiteaReleases, getGiteaUpdaterFeedBase } = require('./gitea-release');
|
||||
|
||||
/**
|
||||
* @param {import('electron').App} app
|
||||
* @param {() => import('electron').BrowserWindow | null} getMainWindow
|
||||
* @param {{
|
||||
* updateFeedUrl?: string,
|
||||
* githubOwner?: string,
|
||||
* githubRepo?: string,
|
||||
* githubToken?: string,
|
||||
* giteaToken?: string,
|
||||
* allowGithubLauncherUpdates?: boolean,
|
||||
* config?: object,
|
||||
* }} opts
|
||||
*/
|
||||
async function setupAutoUpdater(app, getMainWindow, opts = {}) {
|
||||
if (!app.isPackaged) {
|
||||
return {
|
||||
checkNow: async () => ({ skipped: true, reason: 'development build' }),
|
||||
};
|
||||
}
|
||||
|
||||
const ghToken = String(opts.githubToken || '').trim();
|
||||
const giteaTok = String(opts.giteaToken || '').trim();
|
||||
const envGeneric = String(process.env.LAUNCHER_UPDATE_URL || '').trim();
|
||||
const configGeneric = String(opts.updateFeedUrl || '').trim();
|
||||
let genericUrl = envGeneric || configGeneric;
|
||||
let genericAuthHeader = '';
|
||||
|
||||
if (!genericUrl && opts.config && useGiteaReleases(opts.config)) {
|
||||
const gfb = await getGiteaUpdaterFeedBase(opts.config);
|
||||
if (gfb && gfb.url) {
|
||||
genericUrl = gfb.url;
|
||||
const t = String(gfb.token || giteaTok || '').trim();
|
||||
if (t) genericAuthHeader = `token ${t}`;
|
||||
}
|
||||
} else if (genericUrl) {
|
||||
if (giteaTok) genericAuthHeader = `token ${giteaTok}`;
|
||||
else if (ghToken) genericAuthHeader = `Bearer ${ghToken}`;
|
||||
}
|
||||
|
||||
const owner = String(opts.githubOwner || '').trim();
|
||||
const repo = String(opts.githubRepo || '').trim();
|
||||
|
||||
let feedConfigured = false;
|
||||
|
||||
if (genericUrl) {
|
||||
const base = genericUrl.replace(/\/?$/, '/');
|
||||
autoUpdater.setFeedURL({
|
||||
provider: 'generic',
|
||||
url: base,
|
||||
});
|
||||
if (genericAuthHeader) {
|
||||
autoUpdater.requestHeaders = {
|
||||
...autoUpdater.requestHeaders,
|
||||
Authorization: genericAuthHeader,
|
||||
};
|
||||
}
|
||||
feedConfigured = true;
|
||||
} else if (opts.allowGithubLauncherUpdates && ghToken && owner && repo) {
|
||||
autoUpdater.setFeedURL({
|
||||
provider: 'github',
|
||||
owner,
|
||||
repo,
|
||||
private: true,
|
||||
token: ghToken,
|
||||
});
|
||||
feedConfigured = true;
|
||||
}
|
||||
|
||||
if (!feedConfigured) {
|
||||
const reason =
|
||||
'No update channel configured. Set launcher.json → update_feed_url (HTTPS folder with latest.yml), ' +
|
||||
'or fill gitea.base_url/owner/repo (+ GITEA_TOKEN for private), ' +
|
||||
'or set launcher_updates_from_github to true with GITHUB_TOKEN for private GitHub release feeds.';
|
||||
return {
|
||||
checkNow: async () => ({ skipped: true, reason }),
|
||||
};
|
||||
}
|
||||
|
||||
autoUpdater.autoDownload = true;
|
||||
autoUpdater.autoInstallOnAppQuit = true;
|
||||
|
||||
const send = (msg) => {
|
||||
const w = getMainWindow();
|
||||
if (w && !w.isDestroyed()) {
|
||||
w.webContents.send('launcher:progress', msg);
|
||||
}
|
||||
};
|
||||
|
||||
autoUpdater.on('checking-for-update', () => send('Checking for launcher updates…'));
|
||||
autoUpdater.on('update-available', (info) => {
|
||||
send(`Launcher update available: ${info.version}`);
|
||||
});
|
||||
autoUpdater.on('update-not-available', () => {});
|
||||
autoUpdater.on('error', (err) => {
|
||||
const m = (err && (err.message || String(err))) || '';
|
||||
if (/404|releases\.atom|HttpError:\s*404/i.test(m)) {
|
||||
send(
|
||||
'Launcher update: 404 (no latest.yml or wrong URL). For Gitea use gitea.* + token, or set update_feed_url. ' +
|
||||
'For private GitHub set GITHUB_TOKEN.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (m && !/net::ERR|ENOTFOUND|ETIMEDOUT/i.test(m)) {
|
||||
send(`Launcher update: ${m.slice(0, 400)}`);
|
||||
}
|
||||
});
|
||||
autoUpdater.on('download-progress', (p) => {
|
||||
const pct = Math.round(p.percent || 0);
|
||||
send(`Launcher update download: ${pct}%`);
|
||||
});
|
||||
autoUpdater.on('update-downloaded', async (info) => {
|
||||
const win = getMainWindow();
|
||||
const r = await dialog.showMessageBox(win || undefined, {
|
||||
type: 'info',
|
||||
title: 'Launcher update',
|
||||
message: `Version ${info.version} is ready to install.`,
|
||||
detail: 'Restart the launcher now to finish. You can finish patching WoW after restart.',
|
||||
buttons: ['Restart now', 'Later'],
|
||||
defaultId: 0,
|
||||
cancelId: 1,
|
||||
noLink: true,
|
||||
});
|
||||
if (r.response === 0) {
|
||||
autoUpdater.quitAndInstall(false, true);
|
||||
}
|
||||
});
|
||||
|
||||
const checkNow = async () => {
|
||||
const r = await autoUpdater.checkForUpdates();
|
||||
return { ok: true, updateInfo: r && r.updateInfo };
|
||||
};
|
||||
|
||||
const tick = () => {
|
||||
checkNow().catch(() => {});
|
||||
};
|
||||
setTimeout(tick, 5000);
|
||||
setInterval(tick, 6 * 60 * 60 * 1000);
|
||||
|
||||
return { checkNow };
|
||||
}
|
||||
|
||||
module.exports = { setupAutoUpdater };
|
||||
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Production Gitea mirror (non-secret). Edit here and ship — no inject script,
|
||||
* no fractured-release-channel.json, no CI env needed for these fields.
|
||||
* Token stays in env: GITEA_TOKEN or launcher.json → gitea.token_env.
|
||||
*/
|
||||
module.exports = {
|
||||
// Scheme optional — gitea-release normalizes to https:// if missing.
|
||||
base_url: 'https://brassnet.ddns.net:33983',
|
||||
owner: 'Dawnsorrow',
|
||||
repo: 'Fractured-Distro',
|
||||
release_tag: 'latest',
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
|
||||
function mergeConfig(defaults, user) {
|
||||
return {
|
||||
...defaults,
|
||||
...user,
|
||||
update_feed_url:
|
||||
user.update_feed_url != null && user.update_feed_url !== ''
|
||||
? user.update_feed_url
|
||||
: defaults.update_feed_url,
|
||||
launcher_updates_from_github:
|
||||
user.launcher_updates_from_github != null
|
||||
? user.launcher_updates_from_github
|
||||
: defaults.launcher_updates_from_github,
|
||||
github: { ...defaults.github, ...(user.github || {}) },
|
||||
gitea: { ...defaults.gitea, ...(user.gitea || {}) },
|
||||
patch_manifest: { ...defaults.patch_manifest, ...(user.patch_manifest || {}) },
|
||||
launch: { ...defaults.launch, ...(user.launch || {}) },
|
||||
auth: user.auth != null ? { ...defaults.auth, ...user.auth } : defaults.auth,
|
||||
realmlist: user.realmlist != null ? { ...defaults.realmlist, ...user.realmlist } : defaults.realmlist,
|
||||
files: Array.isArray(user.files) && user.files.length ? user.files : defaults.files,
|
||||
};
|
||||
}
|
||||
|
||||
/** Hardcoded Gitea host/repo (see lib/baked-gitea-channel.js). Non-empty baked values win. */
|
||||
function applyBakedGitea(cfg) {
|
||||
let baked;
|
||||
try {
|
||||
baked = require('./baked-gitea-channel');
|
||||
} catch {
|
||||
return cfg;
|
||||
}
|
||||
if (!baked || typeof baked !== 'object') return cfg;
|
||||
cfg.gitea = { ...(cfg.gitea || {}) };
|
||||
for (const k of ['base_url', 'owner', 'repo', 'release_tag']) {
|
||||
const v = baked[k];
|
||||
if (v != null && String(v).trim() !== '') cfg.gitea[k] = String(v).trim();
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
function getConfigPath(app) {
|
||||
if (process.env.FRACTURED_LAUNCHER_CONFIG) return process.env.FRACTURED_LAUNCHER_CONFIG;
|
||||
if (app && app.isPackaged) {
|
||||
return path.join(path.dirname(process.execPath), 'launcher.json');
|
||||
}
|
||||
return path.join(__dirname, '..', 'launcher.json');
|
||||
}
|
||||
|
||||
async function loadConfig(app) {
|
||||
const p = getConfigPath(app);
|
||||
const defPath = path.join(__dirname, '..', 'default-launcher.json');
|
||||
const defaults = JSON.parse(await fs.readFile(defPath, 'utf8'));
|
||||
try {
|
||||
const user = JSON.parse(await fs.readFile(p, 'utf8'));
|
||||
return { configPath: p, config: applyBakedGitea(mergeConfig(defaults, user)) };
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
const initial = applyBakedGitea(mergeConfig(defaults, {}));
|
||||
await fs.writeFile(p, JSON.stringify(initial, null, 2), 'utf8');
|
||||
return { configPath: p, config: JSON.parse(JSON.stringify(initial)) };
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveGameDir(configPath, gameDir) {
|
||||
const defPath = path.join(__dirname, '..', 'default-launcher.json');
|
||||
const defaults = JSON.parse(await fs.readFile(defPath, 'utf8'));
|
||||
const user = JSON.parse(await fs.readFile(configPath, 'utf8'));
|
||||
user.game_dir = gameDir;
|
||||
const merged = applyBakedGitea(mergeConfig(defaults, user));
|
||||
await fs.writeFile(configPath, JSON.stringify(merged, null, 2), 'utf8');
|
||||
return merged;
|
||||
}
|
||||
|
||||
function resolveGameDir(cfg, configPath) {
|
||||
const gd = cfg.game_dir;
|
||||
if (!gd) return '';
|
||||
if (path.isAbsolute(gd)) return path.normalize(gd);
|
||||
return path.normalize(path.join(path.dirname(configPath), gd));
|
||||
}
|
||||
|
||||
module.exports = { getConfigPath, loadConfig, saveGameDir, resolveGameDir, mergeConfig, applyBakedGitea };
|
||||
@@ -0,0 +1,107 @@
|
||||
'use strict';
|
||||
|
||||
const { downloadBodyToFile } = require('./http-download');
|
||||
|
||||
function normalizeGiteaBaseUrl(raw) {
|
||||
let b = String(raw || '').trim().replace(/\/+$/, '');
|
||||
if (!b) return '';
|
||||
if (!/^https?:\/\//i.test(b)) b = `https://${b}`;
|
||||
return b;
|
||||
}
|
||||
|
||||
function giteaApiBase(cfg) {
|
||||
const base = normalizeGiteaBaseUrl(cfg.gitea.base_url);
|
||||
return `${base}/api/v1`;
|
||||
}
|
||||
|
||||
function giteaToken(cfg) {
|
||||
const name = cfg.gitea && cfg.gitea.token_env;
|
||||
if (name && process.env[name]) return String(process.env[name]).trim();
|
||||
return String(process.env.GITEA_TOKEN || '').trim();
|
||||
}
|
||||
|
||||
function giteaHeaders(token, json = false) {
|
||||
const h = { 'User-Agent': 'Fractured-Launcher-Electron' };
|
||||
if (json) h.Accept = 'application/json';
|
||||
if (token) h.Authorization = `token ${token}`;
|
||||
return h;
|
||||
}
|
||||
|
||||
function useGiteaReleases(cfg) {
|
||||
const g = cfg.gitea;
|
||||
if (!g) return false;
|
||||
return !!(String(g.base_url || '').trim() && String(g.owner || '').trim() && String(g.repo || '').trim());
|
||||
}
|
||||
|
||||
async function downloadGiteaReleaseAsset(cfg, assetName, destPath) {
|
||||
const api = giteaApiBase(cfg);
|
||||
const { owner, repo } = cfg.gitea;
|
||||
const tag = (cfg.gitea.release_tag || 'latest').trim() || 'latest';
|
||||
const token = giteaToken(cfg);
|
||||
|
||||
let listUrl;
|
||||
if (tag.toLowerCase() === 'latest') {
|
||||
listUrl = `${api}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/latest`;
|
||||
} else {
|
||||
listUrl = `${api}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/tags/${encodeURIComponent(tag)}`;
|
||||
}
|
||||
|
||||
const res = await fetch(listUrl, { headers: giteaHeaders(token, true) });
|
||||
const text = await res.text();
|
||||
if (!res.ok) {
|
||||
let hint = '';
|
||||
if (res.status === 404) hint = ' (wrong tag / no release / check base_url owner repo)';
|
||||
if (res.status === 401 || res.status === 403) hint = ' (set GITEA_TOKEN or gitea.token_env)';
|
||||
throw new Error(`Gitea release ${res.status}${hint}: ${text.slice(0, 600)}`);
|
||||
}
|
||||
const rel = JSON.parse(text);
|
||||
const list = rel.attachments || rel.assets || [];
|
||||
let downloadUrl = '';
|
||||
for (const a of list) {
|
||||
if (a.name !== assetName) continue;
|
||||
downloadUrl = a.browser_download_url || a.download_url || '';
|
||||
break;
|
||||
}
|
||||
if (!downloadUrl) {
|
||||
const names = list.map((x) => x.name).filter(Boolean);
|
||||
throw new Error(`Gitea release asset "${assetName}" not found; attachments: ${names.join(', ') || '(none)'}`);
|
||||
}
|
||||
|
||||
const h = { Accept: 'application/octet-stream' };
|
||||
if (token) h.Authorization = `token ${token}`;
|
||||
const dl = await fetch(downloadUrl, { headers: h, redirect: 'follow' });
|
||||
await downloadBodyToFile(dl, destPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base URL for electron-updater generic provider (expects latest.yml under this path).
|
||||
* Matches Gitea’s pattern: …/owner/repo/releases/download/{tag}/latest.yml
|
||||
*/
|
||||
async function getGiteaUpdaterFeedBase(cfg) {
|
||||
if (!useGiteaReleases(cfg)) return null;
|
||||
const api = giteaApiBase(cfg);
|
||||
const { owner, repo } = cfg.gitea;
|
||||
const tag = (cfg.gitea.release_tag || 'latest').trim() || 'latest';
|
||||
const token = giteaToken(cfg);
|
||||
let listUrl;
|
||||
if (tag.toLowerCase() === 'latest') {
|
||||
listUrl = `${api}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/latest`;
|
||||
} else {
|
||||
listUrl = `${api}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/tags/${encodeURIComponent(tag)}`;
|
||||
}
|
||||
const res = await fetch(listUrl, { headers: giteaHeaders(token, true) });
|
||||
if (!res.ok) return null;
|
||||
const rel = await res.json();
|
||||
const tagName = rel.tag_name;
|
||||
if (!tagName || typeof tagName !== 'string') return null;
|
||||
const root = normalizeGiteaBaseUrl(cfg.gitea.base_url);
|
||||
const url = `${root}/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/download/${encodeURIComponent(tagName)}/`;
|
||||
return { url, token };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
downloadGiteaReleaseAsset,
|
||||
giteaToken,
|
||||
useGiteaReleases,
|
||||
getGiteaUpdaterFeedBase,
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
function githubToken(cfg) {
|
||||
const name = cfg.github && cfg.github.token_env;
|
||||
if (name && process.env[name]) return process.env[name];
|
||||
return process.env.GITHUB_TOKEN || '';
|
||||
}
|
||||
|
||||
module.exports = { githubToken };
|
||||
@@ -0,0 +1,121 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const { githubToken } = require('./github-token');
|
||||
const { downloadGiteaReleaseAsset, useGiteaReleases } = require('./gitea-release');
|
||||
const { fetchToFile, downloadBodyToFile } = require('./http-download');
|
||||
|
||||
function encodeRepoPath(repoPath) {
|
||||
let p = String(repoPath || '').replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
|
||||
if (!p) return '';
|
||||
return p.split('/').map((seg) => encodeURIComponent(seg)).join('/');
|
||||
}
|
||||
|
||||
function ghHeaders(token, json = false) {
|
||||
const h = {
|
||||
'User-Agent': 'Fractured-Launcher-Electron',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
};
|
||||
if (json) h.Accept = 'application/vnd.github+json';
|
||||
if (token) h.Authorization = `Bearer ${token}`;
|
||||
return h;
|
||||
}
|
||||
|
||||
async function downloadGitHubRepoFile(cfg, repoPath, destPath) {
|
||||
const token = githubToken(cfg);
|
||||
const enc = encodeRepoPath(repoPath);
|
||||
const ref = cfg.github.ref || 'main';
|
||||
const { owner, repo } = cfg.github;
|
||||
|
||||
if (!token) {
|
||||
const url = `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${enc}`;
|
||||
await fetchToFile(url, {}, destPath);
|
||||
return;
|
||||
}
|
||||
|
||||
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${enc}?ref=${encodeURIComponent(ref)}`;
|
||||
const res = await fetch(apiUrl, { headers: ghHeaders(token, true) });
|
||||
const body = await res.text();
|
||||
if (!res.ok) {
|
||||
throw new Error(`GitHub contents API ${res.status}: ${body.slice(0, 800)}`);
|
||||
}
|
||||
const meta = JSON.parse(body);
|
||||
if (meta.type && meta.type !== 'file') {
|
||||
throw new Error(`not a file: ${repoPath}`);
|
||||
}
|
||||
if (meta.download_url) {
|
||||
const h = { Accept: 'application/octet-stream' };
|
||||
if (token) {
|
||||
h.Authorization = `Bearer ${token}`;
|
||||
h['X-GitHub-Api-Version'] = '2022-11-28';
|
||||
}
|
||||
await fetchToFile(meta.download_url, h, destPath);
|
||||
return;
|
||||
}
|
||||
if (meta.content && meta.encoding === 'base64') {
|
||||
const buf = Buffer.from(String(meta.content).replace(/\n/g, ''), 'base64');
|
||||
if (!buf.length) throw new Error('empty base64 content');
|
||||
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
||||
const tmp = destPath + '.downloading';
|
||||
await fs.writeFile(tmp, buf);
|
||||
await fs.rename(tmp, destPath);
|
||||
return;
|
||||
}
|
||||
throw new Error(`unexpected GitHub response for ${repoPath}`);
|
||||
}
|
||||
|
||||
async function downloadReleaseAsset(cfg, assetName, destPath) {
|
||||
if (useGiteaReleases(cfg)) {
|
||||
return downloadGiteaReleaseAsset(cfg, assetName, destPath);
|
||||
}
|
||||
const token = githubToken(cfg);
|
||||
const tag = (cfg.github.release_tag || 'latest').trim() || 'latest';
|
||||
const { owner, repo } = cfg.github;
|
||||
let listUrl;
|
||||
if (tag.toLowerCase() === 'latest') {
|
||||
listUrl = `https://api.github.com/repos/${owner}/${repo}/releases/latest`;
|
||||
} else {
|
||||
listUrl = `https://api.github.com/repos/${owner}/${repo}/releases/tags/${encodeURIComponent(tag)}`;
|
||||
}
|
||||
const res = await fetch(listUrl, { headers: ghHeaders(token, true) });
|
||||
const text = await res.text();
|
||||
if (!res.ok) {
|
||||
let hint = '';
|
||||
if (res.status === 404) {
|
||||
hint =
|
||||
' (wrong tag, private repo without token, or releases live on Gitea — set gitea.base_url, gitea.owner, gitea.repo in launcher.json)';
|
||||
}
|
||||
if (res.status === 401 || res.status === 403) hint = ' (set GITHUB_TOKEN or token_env PAT)';
|
||||
throw new Error(`releases list ${res.status}${hint}: ${text.slice(0, 600)}`);
|
||||
}
|
||||
const rel = JSON.parse(text);
|
||||
const assets = rel.assets || [];
|
||||
let assetURL = '';
|
||||
for (const a of assets) {
|
||||
if (a.name !== assetName) continue;
|
||||
if (token && a.url) {
|
||||
assetURL = a.url;
|
||||
break;
|
||||
}
|
||||
if (a.browser_download_url) {
|
||||
assetURL = a.browser_download_url;
|
||||
break;
|
||||
}
|
||||
assetURL = a.url;
|
||||
break;
|
||||
}
|
||||
if (!assetURL) {
|
||||
const names = assets.map((x) => x.name);
|
||||
throw new Error(`release asset "${assetName}" not found; attachments: ${names.join(', ')}`);
|
||||
}
|
||||
const h = { Accept: 'application/octet-stream' };
|
||||
if (token) {
|
||||
h.Authorization = `Bearer ${token}`;
|
||||
h['X-GitHub-Api-Version'] = '2022-11-28';
|
||||
}
|
||||
const dl = await fetch(assetURL, { headers: h, redirect: 'follow' });
|
||||
await downloadBodyToFile(dl, destPath);
|
||||
}
|
||||
|
||||
module.exports = { downloadGitHubRepoFile, downloadReleaseAsset, encodeRepoPath };
|
||||
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const { createWriteStream } = require('fs');
|
||||
const { pipeline } = require('stream/promises');
|
||||
const { Readable } = require('stream');
|
||||
|
||||
async function downloadBodyToFile(res, destPath) {
|
||||
if (!res.ok) {
|
||||
const errText = await res.text().catch(() => '');
|
||||
throw new Error(`HTTP ${res.status}: ${errText.slice(0, 500)}`);
|
||||
}
|
||||
if (!res.body) {
|
||||
throw new Error('download has no body');
|
||||
}
|
||||
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
||||
const tmp = destPath + '.downloading';
|
||||
let body = res.body;
|
||||
if (body && typeof body.pipe !== 'function') {
|
||||
body = Readable.fromWeb(body);
|
||||
}
|
||||
await pipeline(body, createWriteStream(tmp));
|
||||
const st = await fs.stat(tmp);
|
||||
if (st.size === 0) {
|
||||
await fs.unlink(tmp).catch(() => {});
|
||||
throw new Error('empty download');
|
||||
}
|
||||
await fs.rename(tmp, destPath);
|
||||
}
|
||||
|
||||
async function fetchToFile(url, headers, destPath) {
|
||||
const res = await fetch(url, {
|
||||
headers,
|
||||
redirect: 'follow',
|
||||
});
|
||||
await downloadBodyToFile(res, destPath);
|
||||
}
|
||||
|
||||
module.exports = { fetchToFile, downloadBodyToFile };
|
||||
@@ -0,0 +1,144 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const { createHash } = require('node:crypto');
|
||||
const { downloadReleaseAsset, downloadGitHubRepoFile } = require('./github');
|
||||
|
||||
async function sha256File(absPath) {
|
||||
const buf = await fs.readFile(absPath);
|
||||
return createHash('sha256').update(buf).digest('hex');
|
||||
}
|
||||
|
||||
function stateDir(gameDir) {
|
||||
return path.join(gameDir, '.fractured');
|
||||
}
|
||||
|
||||
function statePath(gameDir) {
|
||||
return path.join(stateDir(gameDir), 'patch-state.json');
|
||||
}
|
||||
|
||||
async function readPatchState(gameDir) {
|
||||
if (!gameDir) return null;
|
||||
try {
|
||||
const t = await fs.readFile(statePath(gameDir), 'utf8');
|
||||
return JSON.parse(t);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function writePatchState(gameDir, manifestVersion, fileShas) {
|
||||
const p = statePath(gameDir);
|
||||
await fs.mkdir(path.dirname(p), { recursive: true });
|
||||
const body = {
|
||||
client_build: manifestVersion,
|
||||
updated_at: new Date().toISOString(),
|
||||
files: fileShas,
|
||||
};
|
||||
const tmp = p + '.tmp';
|
||||
await fs.writeFile(tmp, JSON.stringify(body, null, 2), 'utf8');
|
||||
await fs.rename(tmp, p);
|
||||
}
|
||||
|
||||
function validateManifest(m) {
|
||||
if (!m || m.version == null || String(m.version).trim() === '') return false;
|
||||
if (!m.files || typeof m.files !== 'object') return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download and parse patch-manifest.json (or custom name). Returns null on any failure.
|
||||
*/
|
||||
async function loadManifest(cfg) {
|
||||
const pm = cfg.patch_manifest;
|
||||
if (!pm || !pm.enabled || !String(pm.source || '').trim()) return null;
|
||||
const tmp = path.join(os.tmpdir(), `fr-patch-manifest-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
||||
try {
|
||||
if (pm.from_release) {
|
||||
await downloadReleaseAsset(cfg, String(pm.source).trim(), tmp);
|
||||
} else {
|
||||
await downloadGitHubRepoFile(cfg, String(pm.source).trim(), tmp);
|
||||
}
|
||||
const raw = await fs.readFile(tmp, 'utf8');
|
||||
await fs.unlink(tmp).catch(() => {});
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
await fs.unlink(tmp).catch(() => {});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* True if every from_release file on disk matches manifest sha256.
|
||||
*/
|
||||
async function patchesMatchManifest(cfg, manifest, onStatus) {
|
||||
if (!validateManifest(manifest)) return false;
|
||||
const gameDir = cfg.game_dir;
|
||||
for (const entry of cfg.files || []) {
|
||||
if (!entry.from_release) continue;
|
||||
const spec = manifest.files[entry.source];
|
||||
if (!spec || !spec.sha256) return false;
|
||||
const parts = String(entry.dest).replace(/\\/g, '/').split('/').filter(Boolean);
|
||||
const destAbs = path.join(gameDir, ...parts);
|
||||
let disk;
|
||||
try {
|
||||
disk = await sha256File(destAbs);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
if (disk.toLowerCase() !== String(spec.sha256).trim().toLowerCase()) return false;
|
||||
}
|
||||
if (onStatus) {
|
||||
onStatus(`Client files already match build ${manifest.version} (nothing to download).`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function verifyInstalledAgainstManifest(cfg, manifest) {
|
||||
if (!validateManifest(manifest)) return;
|
||||
for (const entry of cfg.files || []) {
|
||||
if (!entry.from_release) continue;
|
||||
const spec = manifest.files[entry.source];
|
||||
if (!spec || !spec.sha256) {
|
||||
throw new Error(
|
||||
`patch-manifest.json is missing a sha256 for "${entry.source}" — regenerate the manifest for this release.`
|
||||
);
|
||||
}
|
||||
const parts = String(entry.dest).replace(/\\/g, '/').split('/').filter(Boolean);
|
||||
const destAbs = path.join(cfg.game_dir, ...parts);
|
||||
const disk = await sha256File(destAbs);
|
||||
if (disk.toLowerCase() !== String(spec.sha256).trim().toLowerCase()) {
|
||||
throw new Error(
|
||||
`${entry.source}: checksum mismatch after install (expected ${spec.sha256.slice(0, 12)}…, got ${disk.slice(0, 12)}…). Try syncing again.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function recordPatchState(cfg, manifest) {
|
||||
if (!validateManifest(manifest)) return;
|
||||
const shas = {};
|
||||
for (const entry of cfg.files || []) {
|
||||
if (!entry.from_release) continue;
|
||||
const parts = String(entry.dest).replace(/\\/g, '/').split('/').filter(Boolean);
|
||||
const destAbs = path.join(cfg.game_dir, ...parts);
|
||||
try {
|
||||
shas[entry.source] = await sha256File(destAbs);
|
||||
} catch {
|
||||
/* skip */
|
||||
}
|
||||
}
|
||||
await writePatchState(cfg.game_dir, String(manifest.version), shas);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadManifest,
|
||||
validateManifest,
|
||||
patchesMatchManifest,
|
||||
verifyInstalledAgainstManifest,
|
||||
recordPatchState,
|
||||
readPatchState,
|
||||
statePath,
|
||||
};
|
||||
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const { downloadGitHubRepoFile, downloadReleaseAsset } = require('./github');
|
||||
|
||||
function pad2(n) {
|
||||
return String(n).padStart(2, '0');
|
||||
}
|
||||
function backupSuffix() {
|
||||
const d = new Date();
|
||||
return `${d.getFullYear()}${pad2(d.getMonth() + 1)}${pad2(d.getDate())}-${pad2(d.getHours())}${pad2(d.getMinutes())}${pad2(d.getSeconds())}`;
|
||||
}
|
||||
|
||||
function wowExePath(cfg) {
|
||||
const exe = (cfg.launch && cfg.launch.exe) || 'Wow.exe';
|
||||
const parts = exe.replace(/\\/g, '/').split('/').filter(Boolean);
|
||||
return path.join(cfg.game_dir, ...parts);
|
||||
}
|
||||
|
||||
function wowInstallValid(cfg) {
|
||||
if (!cfg.game_dir) return false;
|
||||
return require('fs').existsSync(wowExePath(cfg));
|
||||
}
|
||||
|
||||
async function installFile(cfg, entry) {
|
||||
const parts = String(entry.dest).replace(/\\/g, '/').split('/').filter(Boolean);
|
||||
const destAbs = path.join(cfg.game_dir, ...parts);
|
||||
if (entry.backup) {
|
||||
try {
|
||||
const st = await fs.stat(destAbs);
|
||||
if (st.isFile()) {
|
||||
const bak = `${destAbs}.bak-${backupSuffix()}`;
|
||||
await fs.rename(destAbs, bak);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') throw e;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await fs.unlink(destAbs);
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') throw e;
|
||||
}
|
||||
}
|
||||
const tmp = destAbs + '.new';
|
||||
if (entry.from_release) {
|
||||
await downloadReleaseAsset(cfg, entry.source, tmp);
|
||||
} else {
|
||||
await downloadGitHubRepoFile(cfg, entry.source, tmp);
|
||||
}
|
||||
await fs.rename(tmp, destAbs);
|
||||
}
|
||||
|
||||
async function applyRealmlist(cfg) {
|
||||
if (!cfg.realmlist || !cfg.realmlist.enabled) return;
|
||||
let line = String(cfg.realmlist.line || '').trim();
|
||||
if (!line) throw new Error('realmlist.line empty');
|
||||
if (!line.toLowerCase().startsWith('set realmlist ')) {
|
||||
line = `set realmlist ${line}`;
|
||||
}
|
||||
const content = line + '\n';
|
||||
let paths = cfg.realmlist.paths;
|
||||
if (!paths || !paths.length) paths = ['Data/enUS/realmlist.wtf'];
|
||||
for (const rel of paths) {
|
||||
const r = String(rel).trim().replace(/\\/g, '/');
|
||||
if (!r) continue;
|
||||
const segs = r.split('/').filter(Boolean);
|
||||
const abs = path.join(cfg.game_dir, ...segs);
|
||||
await fs.mkdir(path.dirname(abs), { recursive: true });
|
||||
await fs.writeFile(abs, content, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
async function applyPatches(cfg, onStatus) {
|
||||
for (const f of cfg.files || []) {
|
||||
if (onStatus) onStatus(`Updating ${f.dest} …`);
|
||||
try {
|
||||
await installFile(cfg, f);
|
||||
} catch (e) {
|
||||
throw new Error(`sync ${f.dest}: ${e.message || e}`);
|
||||
}
|
||||
}
|
||||
if (cfg.realmlist && cfg.realmlist.enabled) {
|
||||
if (onStatus) onStatus('Applying realmlist …');
|
||||
await applyRealmlist(cfg);
|
||||
}
|
||||
if (onStatus) onStatus('All patches applied.');
|
||||
}
|
||||
|
||||
async function doAuth(cfg, username, password) {
|
||||
if (!cfg.auth || !cfg.auth.enabled) return;
|
||||
const u = String(username || '').trim();
|
||||
const p = String(password || '');
|
||||
if (!u || !p) throw new Error('username and password required');
|
||||
const body = {
|
||||
[cfg.auth.username_field || 'username']: u,
|
||||
[cfg.auth.password_field || 'password']: p,
|
||||
};
|
||||
const res = await fetch(cfg.auth.url, {
|
||||
method: cfg.auth.method || 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const t = await res.text();
|
||||
if (res.status < 200 || res.status >= 300) {
|
||||
throw new Error(`login failed ${res.status}: ${t.slice(0, 400)}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
applyPatches,
|
||||
applyRealmlist,
|
||||
wowExePath,
|
||||
wowInstallValid,
|
||||
doAuth,
|
||||
};
|
||||
@@ -0,0 +1,156 @@
|
||||
'use strict';
|
||||
|
||||
const { app, BrowserWindow, ipcMain, dialog, Menu } = require('electron');
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
const { loadConfig, saveGameDir, resolveGameDir } = require('./lib/config-store');
|
||||
const { applyPatches, wowExePath, wowInstallValid, doAuth } = require('./lib/patch');
|
||||
const { readPatchState } = require('./lib/patch-manifest');
|
||||
const { setupAutoUpdater } = require('./lib/auto-update');
|
||||
|
||||
let mainWindow;
|
||||
let autoUpdateApi = {
|
||||
checkNow: async () => ({ skipped: true, reason: 'not initialized' }),
|
||||
};
|
||||
|
||||
function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 720,
|
||||
height: 640,
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.cjs'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
sandbox: false,
|
||||
},
|
||||
});
|
||||
Menu.setApplicationMenu(null);
|
||||
mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
||||
mainWindow.once('ready-to-show', () => mainWindow.show());
|
||||
}
|
||||
|
||||
function sendProgress(msg) {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('launcher:progress', msg);
|
||||
}
|
||||
}
|
||||
|
||||
async function readMergedConfig() {
|
||||
const { configPath, config } = await loadConfig(app);
|
||||
const gameDir = resolveGameDir(config, configPath);
|
||||
const merged = { ...config, game_dir: gameDir };
|
||||
return { configPath, config: merged };
|
||||
}
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
createWindow();
|
||||
const { config } = await loadConfig(app);
|
||||
const ghEnv = config.github && config.github.token_env;
|
||||
const githubToken =
|
||||
(ghEnv && String(process.env[ghEnv] || '').trim()) ||
|
||||
String(process.env.GH_TOKEN || process.env.GITHUB_TOKEN || '').trim();
|
||||
const giteaEnv = config.gitea && config.gitea.token_env;
|
||||
const giteaToken =
|
||||
(giteaEnv && String(process.env[giteaEnv] || '').trim()) ||
|
||||
String(process.env.GITEA_TOKEN || '').trim();
|
||||
const updateFeedUrl = String(process.env.LAUNCHER_UPDATE_URL || config.update_feed_url || '').trim();
|
||||
autoUpdateApi = await setupAutoUpdater(app, () => mainWindow, {
|
||||
updateFeedUrl,
|
||||
config,
|
||||
githubOwner: config.github && config.github.owner,
|
||||
githubRepo: config.github && config.github.repo,
|
||||
githubToken,
|
||||
giteaToken,
|
||||
allowGithubLauncherUpdates: config.launcher_updates_from_github === true,
|
||||
});
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
||||
});
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit();
|
||||
});
|
||||
|
||||
ipcMain.handle('launcher:load', async () => {
|
||||
const { configPath, config } = await readMergedConfig();
|
||||
let clientBuild = '';
|
||||
if (wowInstallValid(config)) {
|
||||
const st = await readPatchState(config.game_dir);
|
||||
if (st && st.client_build) clientBuild = String(st.client_build);
|
||||
}
|
||||
return {
|
||||
configPath,
|
||||
gameDir: config.game_dir || '',
|
||||
authEnabled: !!(config.auth && config.auth.enabled),
|
||||
wowExe: (config.launch && config.launch.exe) || 'Wow.exe',
|
||||
wowOk: wowInstallValid(config),
|
||||
clientBuild,
|
||||
};
|
||||
});
|
||||
|
||||
ipcMain.handle('launcher:saveGameDir', async (_e, dir) => {
|
||||
const trimmed = String(dir || '').trim();
|
||||
if (!trimmed) throw new Error('folder path is empty');
|
||||
const { configPath } = await loadConfig(app);
|
||||
const norm = path.normalize(trimmed);
|
||||
const probe = { ...(await readMergedConfig()).config, game_dir: norm };
|
||||
if (!wowInstallValid(probe)) {
|
||||
throw new Error(`That folder does not contain ${(probe.launch && probe.launch.exe) || 'Wow.exe'}`);
|
||||
}
|
||||
const c = await saveGameDir(configPath, norm);
|
||||
const merged = { ...c, game_dir: resolveGameDir(c, configPath) };
|
||||
return { ok: true, gameDir: merged.game_dir, wowOk: wowInstallValid(merged) };
|
||||
});
|
||||
|
||||
ipcMain.handle('launcher:pickFolder', async (_e, startDir) => {
|
||||
const win = BrowserWindow.getFocusedWindow() || mainWindow;
|
||||
const r = await dialog.showOpenDialog(win, {
|
||||
title: 'Select World of Warcraft 3.3.5a folder',
|
||||
properties: ['openDirectory', 'createDirectory'],
|
||||
defaultPath: startDir && String(startDir).trim() ? String(startDir).trim() : undefined,
|
||||
});
|
||||
if (r.canceled || !r.filePaths || !r.filePaths[0]) return { canceled: true, path: '' };
|
||||
return { canceled: false, path: r.filePaths[0] };
|
||||
});
|
||||
|
||||
ipcMain.handle('launcher:auth', async (_e, { user, pass }) => {
|
||||
const { config } = await readMergedConfig();
|
||||
await doAuth(config, user, pass);
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
ipcMain.handle('launcher:sync', async () => {
|
||||
const { config } = await readMergedConfig();
|
||||
if (!wowInstallValid(config)) {
|
||||
throw new Error('Set a valid WoW folder (must contain Wow.exe) first.');
|
||||
}
|
||||
await applyPatches(config, sendProgress);
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
ipcMain.handle('launcher:checkUpdates', async () => {
|
||||
try {
|
||||
return await autoUpdateApi.checkNow();
|
||||
} catch (e) {
|
||||
const msg = e && (e.message || String(e));
|
||||
return { ok: false, error: msg };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('launcher:play', async () => {
|
||||
const { config } = await readMergedConfig();
|
||||
const exe = wowExePath(config);
|
||||
const args = (config.launch && config.launch.args) || [];
|
||||
const child = spawn(exe, args, {
|
||||
cwd: config.game_dir,
|
||||
detached: true,
|
||||
stdio: 'ignore',
|
||||
windowsHide: true,
|
||||
shell: false,
|
||||
});
|
||||
child.unref();
|
||||
return { ok: true };
|
||||
});
|
||||
+5355
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "fractured-launcher-electron",
|
||||
"version": "1.0.2",
|
||||
"description": "Fractured WoW launcher (Electron) — no console window, native folder picker, auto-update",
|
||||
"main": "main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Dawnforger/Fractured.git"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"pack:win": "electron-builder --win nsis portable --x64 --publish never",
|
||||
"pack:linux": "electron-builder --linux AppImage --x64 --publish never",
|
||||
"publish:win": "electron-builder --win nsis portable --x64 --publish never"
|
||||
},
|
||||
"author": "",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"electron": "^33.2.1",
|
||||
"electron-builder": "^25.1.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.3.9"
|
||||
},
|
||||
"build": {
|
||||
"appId": "net.fractured.launcher",
|
||||
"productName": "Fractured Launcher",
|
||||
"directories": {
|
||||
"output": "dist"
|
||||
},
|
||||
"publish": null,
|
||||
"files": [
|
||||
"main.js",
|
||||
"preload.cjs",
|
||||
"index.html",
|
||||
"renderer.js",
|
||||
"styles.css",
|
||||
"default-launcher.json",
|
||||
"lib/baked-gitea-channel.js",
|
||||
"lib/gitea-release.js",
|
||||
"lib/patch-manifest.js",
|
||||
"lib/**/*"
|
||||
],
|
||||
"win": {
|
||||
"target": [
|
||||
{
|
||||
"target": "nsis",
|
||||
"arch": ["x64"]
|
||||
},
|
||||
{
|
||||
"target": "portable",
|
||||
"arch": ["x64"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"artifactName": "Fractured-Launcher-${version}-Setup.${ext}"
|
||||
},
|
||||
"portable": {
|
||||
"artifactName": "Fractured-Launcher-${version}-Windows-Portable.${ext}"
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": ["x64"]
|
||||
}
|
||||
],
|
||||
"category": "Game"
|
||||
},
|
||||
"appImage": {
|
||||
"artifactName": "Fractured-Launcher-${version}-Linux-x86_64.${ext}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('launcher', {
|
||||
load: () => ipcRenderer.invoke('launcher:load'),
|
||||
saveGameDir: (dir) => ipcRenderer.invoke('launcher:saveGameDir', dir),
|
||||
pickFolder: (startDir) => ipcRenderer.invoke('launcher:pickFolder', startDir),
|
||||
auth: (user, pass) => ipcRenderer.invoke('launcher:auth', { user, pass }),
|
||||
sync: () => ipcRenderer.invoke('launcher:sync'),
|
||||
checkUpdates: () => ipcRenderer.invoke('launcher:checkUpdates'),
|
||||
play: () => ipcRenderer.invoke('launcher:play'),
|
||||
onProgress: (cb) => {
|
||||
ipcRenderer.on('launcher:progress', (_e, msg) => cb(msg));
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,130 @@
|
||||
'use strict';
|
||||
|
||||
const logEl = document.getElementById('log');
|
||||
const gameDirEl = document.getElementById('gameDir');
|
||||
const btnBrowse = document.getElementById('btnBrowse');
|
||||
const btnSave = document.getElementById('btnSaveFolder');
|
||||
const btnSync = document.getElementById('btnSync');
|
||||
const btnPlay = document.getElementById('btnPlay');
|
||||
const btnCheckLauncher = document.getElementById('btnCheckLauncher');
|
||||
const authCard = document.getElementById('authCard');
|
||||
const btnAuth = document.getElementById('btnAuth');
|
||||
const wowExeName = document.getElementById('wowExeName');
|
||||
|
||||
function log(msg) {
|
||||
logEl.textContent += (logEl.textContent ? '\n' : '') + msg;
|
||||
logEl.scrollTop = logEl.scrollHeight;
|
||||
}
|
||||
|
||||
function setError(e) {
|
||||
const m = e && (e.message || String(e));
|
||||
log('Error: ' + m);
|
||||
}
|
||||
|
||||
let authEnabled = false;
|
||||
let signedIn = false;
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
const s = await window.launcher.load();
|
||||
authEnabled = s.authEnabled;
|
||||
signedIn = !s.authEnabled;
|
||||
wowExeName.textContent = s.wowExe || 'Wow.exe';
|
||||
gameDirEl.value = s.gameDir || '';
|
||||
authCard.classList.toggle('hidden', !authEnabled);
|
||||
btnSync.disabled = !s.wowOk || (authEnabled && !signedIn);
|
||||
btnPlay.classList.add('hidden');
|
||||
btnPlay.disabled = true;
|
||||
logEl.textContent = '';
|
||||
if (!s.gameDir) log('Choose your WoW installation folder.');
|
||||
else if (!s.wowOk) log('Folder does not look valid yet — pick the directory that contains ' + (s.wowExe || 'Wow.exe') + ', then Save folder.');
|
||||
else if (authEnabled && !signedIn) log('Sign in, then download updates.');
|
||||
else log('Ready — tap Download updates to sync from GitHub.');
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
}
|
||||
}
|
||||
|
||||
window.launcher.onProgress((msg) => log(msg));
|
||||
|
||||
btnBrowse.addEventListener('click', async () => {
|
||||
try {
|
||||
const start = gameDirEl.value.trim();
|
||||
const r = await window.launcher.pickFolder(start);
|
||||
if (!r.canceled && r.path) {
|
||||
gameDirEl.value = r.path;
|
||||
log('Selected: ' + r.path);
|
||||
}
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
}
|
||||
});
|
||||
|
||||
btnSave.addEventListener('click', async () => {
|
||||
try {
|
||||
const dir = gameDirEl.value.trim();
|
||||
if (!dir) {
|
||||
log('Pick a folder with Browse… first.');
|
||||
return;
|
||||
}
|
||||
const r = await window.launcher.saveGameDir(dir);
|
||||
gameDirEl.value = r.gameDir;
|
||||
btnSync.disabled = !r.wowOk || (authEnabled && !signedIn);
|
||||
log('Saved installation folder.');
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
}
|
||||
});
|
||||
|
||||
btnAuth.addEventListener('click', async () => {
|
||||
try {
|
||||
const u = document.getElementById('username').value;
|
||||
const p = document.getElementById('password').value;
|
||||
await window.launcher.auth(u, p);
|
||||
signedIn = true;
|
||||
log('Signed in.');
|
||||
btnSync.disabled = !gameDirEl.value.trim() || (authEnabled && !signedIn);
|
||||
const s = await window.launcher.load();
|
||||
btnSync.disabled = !s.wowOk;
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
}
|
||||
});
|
||||
|
||||
btnSync.addEventListener('click', async () => {
|
||||
btnSync.disabled = true;
|
||||
log('—');
|
||||
try {
|
||||
await window.launcher.sync();
|
||||
btnPlay.classList.remove('hidden');
|
||||
btnPlay.disabled = false;
|
||||
log('Done. You can launch the game.');
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
} finally {
|
||||
const s = await window.launcher.load().catch(() => null);
|
||||
btnSync.disabled = !s || !s.wowOk || (authEnabled && !signedIn);
|
||||
}
|
||||
});
|
||||
|
||||
btnPlay.addEventListener('click', async () => {
|
||||
try {
|
||||
await window.launcher.play();
|
||||
window.close();
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
}
|
||||
});
|
||||
|
||||
btnCheckLauncher.addEventListener('click', async () => {
|
||||
try {
|
||||
log('Checking for launcher updates…');
|
||||
const r = await window.launcher.checkUpdates();
|
||||
if (r && r.skipped) log('Launcher auto-update: ' + (r.reason || 'skipped (use a packaged build).'));
|
||||
else if (r && r.ok === false && r.error) setError(new Error(r.error));
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
}
|
||||
});
|
||||
|
||||
refresh();
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
# Push a one-file README so the Gitea repo is non-empty (fixes HTTP 422 "repo is empty"
|
||||
# when CI creates a release). Safe to re-run only if the repo still has no commits;
|
||||
# if it already has history, skip or use the Gitea web UI instead.
|
||||
#
|
||||
# Usage:
|
||||
# export GITEA_BASE_URL=https://git.example.com
|
||||
# export GITEA_OWNER=myorg
|
||||
# export GITEA_REPO=fractured-patches
|
||||
# ./bootstrap-gitea-repo.sh
|
||||
#
|
||||
# Or pass an explicit clone URL (HTTPS or SSH):
|
||||
# ./bootstrap-gitea-repo.sh https://git.example.com/myorg/fractured-patches.git
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
BRANCH="${GITEA_TARGET_REF:-main}"
|
||||
|
||||
if [ "${1:-}" != "" ]; then
|
||||
URL="$1"
|
||||
else
|
||||
: "${GITEA_BASE_URL:?Set GITEA_BASE_URL or pass clone URL as first argument}"
|
||||
: "${GITEA_OWNER:?Set GITEA_OWNER or pass clone URL as first argument}"
|
||||
: "${GITEA_REPO:?Set GITEA_REPO or pass clone URL as first argument}"
|
||||
BASE="${GITEA_BASE_URL%/}"
|
||||
URL="${BASE}/${GITEA_OWNER}/${GITEA_REPO}.git"
|
||||
fi
|
||||
|
||||
TMP=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
cd "$TMP"
|
||||
|
||||
git init -q
|
||||
git checkout -q -b "$BRANCH"
|
||||
|
||||
cat >README.md <<'EOF'
|
||||
# Fractured release mirror
|
||||
|
||||
Release assets (launcher builds, patches, `patch-manifest.json`, etc.) are uploaded here by **GitHub Actions** (“Sync release to Gitea”) from the main Fractured repository.
|
||||
|
||||
This initial commit exists because **Gitea requires at least one commit** in the repository before releases can be created.
|
||||
EOF
|
||||
|
||||
git add README.md
|
||||
git commit -q -m "chore: initial commit (required for Gitea releases)"
|
||||
|
||||
git remote add origin "$URL"
|
||||
git push -u origin "$BRANCH"
|
||||
|
||||
echo "Pushed initial README to $URL (branch $BRANCH)."
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Build patch-manifest.json for a release (same names as files[].source in launcher.json).
|
||||
*
|
||||
* Usage (from a folder containing the patch binaries):
|
||||
* node generate-patch-manifest.js v0.9.0-client patch-Z.MPQ Wow-patched.exe
|
||||
*
|
||||
* Prints JSON to stdout — redirect to file:
|
||||
* node generate-patch-manifest.js v0.9.0-client patch-Z.MPQ Wow-patched.exe > patch-manifest.json
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const version = process.argv[2];
|
||||
const names = process.argv.slice(3);
|
||||
if (!version || names.length === 0) {
|
||||
console.error('Usage: generate-patch-manifest.js <version-label> <file1> [file2 ...]');
|
||||
console.error(' Example: generate-patch-manifest.js v0.9.0-client patch-Z.MPQ Wow-patched.exe');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const out = { version, files: {} };
|
||||
for (const f of names) {
|
||||
const base = path.basename(f);
|
||||
const buf = fs.readFileSync(f);
|
||||
const sha256 = crypto.createHash('sha256').update(buf).digest('hex');
|
||||
out.files[base] = { sha256 };
|
||||
}
|
||||
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# Local Linux AppImage build (uses current tree — no tag snapshot). Run from repo root or this dir.
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
echo "==> npm ci"
|
||||
npm ci
|
||||
echo "==> npm run pack:linux (AppImage x64)"
|
||||
npm run pack:linux
|
||||
echo "==> dist/:"
|
||||
ls -la dist/
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
# Upload local files to a GitHub release on the public distro repo (default: Dawnforger/Fractured-Distro).
|
||||
#
|
||||
# Usage (from repo root or this directory):
|
||||
# export GH_TOKEN=ghp_... # PAT with repo/releases on the distro repo
|
||||
# ./tools/fractured-launcher-electron/scripts/publish-to-distro.sh v1.0.0 patch-Z.MPQ Wow-patched.exe
|
||||
#
|
||||
# Optional:
|
||||
# DISTRO_REPO=YourOrg/Fratured-Distro # if your GitHub slug differs
|
||||
# SRC_TAG=v1.0.0 ./publish-to-distro.sh v1.0.0 # copy all assets from SOURCE_REPO release SRC_TAG
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
DISTRO_REPO="${DISTRO_REPO:-Dawnforger/Fractured-Distro}"
|
||||
SOURCE_REPO="${SOURCE_REPO:-Dawnforger/Fractured}"
|
||||
|
||||
if ! command -v gh >/dev/null 2>&1; then
|
||||
echo "Install GitHub CLI: https://cli.github.com/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${GH_TOKEN:-}" ]; then
|
||||
echo "Set GH_TOKEN to a PAT with releases write access to ${DISTRO_REPO}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "Usage: $0 <release-tag> [files...]"
|
||||
echo " or: SRC_TAG=v1.0.0 $0 <release-tag> # copies all assets from ${SOURCE_REPO} release SRC_TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TAG="$1"
|
||||
shift
|
||||
if [ "$#" -eq 0 ] && [ -z "${SRC_TAG:-}" ]; then
|
||||
echo "After the tag, list files to upload, or set SRC_TAG=... to copy all assets from ${SOURCE_REPO}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
cleanup() { rm -rf "$tmpdir"; }
|
||||
trap cleanup EXIT
|
||||
|
||||
if [ "$#" -eq 0 ] && [ -n "${SRC_TAG:-}" ]; then
|
||||
echo "Downloading assets from ${SOURCE_REPO}@${SRC_TAG} …"
|
||||
gh release download "$SRC_TAG" -R "$SOURCE_REPO" -D "$tmpdir"
|
||||
else
|
||||
for f in "$@"; do
|
||||
if [ ! -f "$f" ]; then
|
||||
echo "Not a file: $f"
|
||||
exit 1
|
||||
fi
|
||||
cp -a "$f" "$tmpdir/"
|
||||
done
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
files=("$tmpdir"/*)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No files to upload."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if gh release view "$TAG" -R "$DISTRO_REPO" &>/dev/null; then
|
||||
gh release upload "$TAG" -R "$DISTRO_REPO" "${files[@]}" --clobber
|
||||
echo "Uploaded to https://github.com/${DISTRO_REPO}/releases/tag/${TAG}"
|
||||
else
|
||||
gh release create "$TAG" -R "$DISTRO_REPO" \
|
||||
--title "Fractured ${TAG}" \
|
||||
--notes "Published from ${SOURCE_REPO} (local script)." \
|
||||
"${files[@]}"
|
||||
echo "Created https://github.com/${DISTRO_REPO}/releases/tag/${TAG}"
|
||||
fi
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user