forked from phenom/healer-man
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3f8a69b51 | ||
|
|
197a64117c | ||
|
|
ed0d2fea3d | ||
|
|
b417b7c3c3 | ||
|
|
361734bc41 | ||
|
|
03a4e3f51c | ||
|
|
40d9f346d8 | ||
|
|
46c272f889 | ||
|
|
05b25c4341 | ||
|
|
6dcb1034e5 | ||
|
|
d3d6aaebdb | ||
|
|
7d7fd567f0 | ||
|
|
544c1bee91 | ||
|
|
8c9809d3e0 | ||
|
|
ed8b9e3719 | ||
|
|
147c2804d4 | ||
|
|
e7cc588bfc | ||
|
|
bac0da9300 | ||
|
|
c4346751a9 | ||
|
|
7f6001723a | ||
|
|
f6f2375145 | ||
|
|
699fc858c9 | ||
|
|
d9bebb4e81 | ||
|
|
eaddad4134 | ||
|
|
fc8a7a722a | ||
|
|
b57d2b2839 | ||
|
|
f49e54f35f | ||
|
|
7b41b102fa | ||
|
|
67d7a8ebe7 | ||
|
|
65e24f644e | ||
|
|
13970d03c8 | ||
|
|
5f1a14d8de | ||
|
|
d060296ade | ||
|
|
c245a199b6 | ||
|
|
eca6b5db9b | ||
|
|
b12fb84859 |
No files matched your search
@@ -0,0 +1,10 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{bat,cmd}]
|
||||
end_of_line = crlf
|
||||
@@ -0,0 +1,3 @@
|
||||
* text=auto eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
@@ -20,5 +20,6 @@ releases/
|
||||
/content/
|
||||
dist-android/
|
||||
.android-public/
|
||||
.web-public/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"files.eol": "\n",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimFinalNewlines": true,
|
||||
"files.trimTrailingWhitespace": true
|
||||
}
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
These rules apply to every change under this project root.
|
||||
|
||||
## Text-file hygiene
|
||||
|
||||
- Every text file must end with exactly one newline, with no extra blank line
|
||||
at EOF and no trailing whitespace.
|
||||
- Before handing off changes, run `npm run release:check`. This check includes
|
||||
untracked files; `git diff --check` alone does not.
|
||||
|
||||
## Workspace and reference boundaries
|
||||
|
||||
- The active project root is `D:\Projects\HealerMan`. All implementation,
|
||||
@@ -44,4 +51,3 @@ These rules apply to every change under this project root.
|
||||
- `document.pointerLockElement` remains `null`;
|
||||
- releasing right mouse stops camera motion;
|
||||
- no browser mouse-capture prompt appears.
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# Balance benchmarks
|
||||
|
||||
The balance benchmark suite provides repeatable evidence for tuning World of Warcraft, CoA, and Runes of Magic classes. It runs fixed builds through versioned damage, tanking, healing, movement, stacking, and resource-endurance scenarios. The goal is to reveal balance changes and outliers, not to name a universally best class.
|
||||
|
||||
## Default cohort
|
||||
|
||||
The default comparison cohort is level 60, matching CoA's authored cap while keeping the three content sources in one cohort. It uses the `coefficient-empty` preset: no equipment, so the first cohort isolates base kits, scaling coefficients, talents, and resources. Gear-normalized cohorts should be added separately rather than mixed with this one.
|
||||
|
||||
Every comparison must use the same gear preset, scenario version, baseline policy ID, duration scale, and seed set. A changed level, gear preset, scenario, policy, or app version creates a new cohort and should not be compared directly with an older one.
|
||||
|
||||
Run the suite with:
|
||||
|
||||
```sh
|
||||
npm run balance:benchmark
|
||||
```
|
||||
|
||||
Use `npm run balance:smoke` for a short end-to-end check. The full command accepts comma-separated `--class`, `--role`, `--scenario`, and `--seeds` filters, plus `--level`, `--duration-scale`, and `--output`. For example:
|
||||
|
||||
```sh
|
||||
npm run balance:benchmark -- --class priest,rom-priest --role healer --seeds 1,2,3
|
||||
```
|
||||
|
||||
The command is the intended stable entry point for the benchmark runner. Its output directory contains:
|
||||
|
||||
- `matrix.json`: raw run reports, build and scenario definitions, seed metadata, validation results, ability details, resources, and event counts.
|
||||
- `matrix.csv`: one row per scenario, build, and metric, with observed/null counts plus median, p10, and p90 values.
|
||||
- `summary.md`: a concise role-separated comparison for review and pull-request evidence.
|
||||
|
||||
Keep the JSON artifact when investigating a result. The CSV and Markdown are derived views and can be regenerated from it.
|
||||
|
||||
## Reading results
|
||||
|
||||
The reported center is the median across validated seeded runs. The p10–p90 interval shows run-to-run spread; a wide interval calls for more seeds or investigation before tuning. Invalid runs are counted but excluded from distributions. An early death or other scenario stop can be a valid outcome and should not be discarded merely because the full duration was not reached.
|
||||
|
||||
Interpret each role as a vector of outcomes, not a single ranking:
|
||||
|
||||
- Damage builds emphasize DPS and priority-target damage, with deaths and mechanics failures alongside throughput.
|
||||
- Tanks emphasize damage taken per second, mitigation, avoidance, threat control, minimum health, survival, and their damage contribution.
|
||||
- Healers emphasize healing per second, consumed absorbs, overheal, party health, deaths, and time to resource failure.
|
||||
- Movement and stack scenarios add movement interrupts and stack compliance to the relevant role vector.
|
||||
|
||||
No composite score is produced. Compare a build only with other builds in the same scenario version and cohort, and inspect the underlying metrics before making a balance change. A null time to resource failure means the resource was not exhausted during that run; it is not zero. Similarly, a null time to first death means no death occurred.
|
||||
|
||||
## Limitations
|
||||
|
||||
- The baseline policy is a deterministic, maintainable reference policy, not an optimal player rotation. Skilled play may outperform it, and policy changes can move results without a class-data change.
|
||||
- The initial `coefficient-empty` cohort does not model production gear breakpoints or itemization. Add explicit equal-budget gear presets for that work.
|
||||
- Runes of Magic coverage is primary-class-only initially. Secondary-class and dual-class interactions require their own explicitly versioned cohort when supported.
|
||||
- Results cover mechanics represented by the simulator and scenarios; they do not replace live encounter playtesting, usability review, or PvP testing.
|
||||
- Small seed sets can miss rare random outcomes. Treat p10 and p90 as descriptive bounds for the sampled runs, not guarantees.
|
||||
- Total metrics depend on scenario duration. Compare rates or fixed-duration totals only within the same scenario definition.
|
||||
- Gear-stat budgets must be equivalent, but equivalent budgets do not guarantee identical interactions with every class kit.
|
||||
+74
-34
@@ -1,10 +1,14 @@
|
||||
# Local-first content updates and Android releases
|
||||
# Local-first content updates for browser and Android
|
||||
|
||||
Healer Man separates the installed game client from large game content. The
|
||||
Android APK contains the React/Three.js runtime, native Android bridge, menus,
|
||||
local profile and save support, UI assets, and the Wailing Caverns starter
|
||||
pack. Other GLBs, textures, audio, character packs, dungeons, and Manastorm
|
||||
stages are published by TrueNAS as optional content.
|
||||
Healer Man separates the installed game client from large game content. Both
|
||||
the production browser build and Android APK contain a compact required set:
|
||||
the React/Three.js runtime, menus, local profile and save support, UI assets,
|
||||
starter equipment, Wailing Caverns starter content, and the basic Human
|
||||
character model with its default male and female appearances. Android also
|
||||
contains its native bridge. Other GLBs, textures, audio, character/skin packs,
|
||||
and dungeons are published by TrueNAS as optional content. Internal Manastorm
|
||||
environment packs remain in the catalog only because regular dungeons reuse
|
||||
them as automatic dependencies.
|
||||
|
||||
Single-player is local-first. Starting the game, loading a character, entering
|
||||
installed content, simulating combat, and saving progress never require the
|
||||
@@ -27,16 +31,12 @@ UI behavior, shaders or loaders, native Android code, permissions, or the
|
||||
content-manifest schema. A manifest can set `minimumAppVersion` so an older APK
|
||||
will not activate incompatible content.
|
||||
|
||||
The browser build remains a full build served by TrueNAS. It receives client
|
||||
code when the app checkout is pulled, rebuilt, and restarted. The Android build
|
||||
is deliberately separate and compact so the APK does not inherit the complete
|
||||
multi-gigabyte browser asset catalog.
|
||||
|
||||
Measured on August 14, 2026, the production browser `dist` is 3.88 GiB across
|
||||
18,895 files; GLBs account for 3.75 GiB. The compact Android `dist-android` is
|
||||
148.0 MiB across 5,561 files, including 47.8 MiB of starter GLBs. The final
|
||||
signed APK will be smaller because Android packages compress eligible files;
|
||||
record its exact size after the first release build.
|
||||
Production builds deliberately stage only required content. `npm run build`
|
||||
prepares `.web-public` and emits the compact browser `dist`; Android prepares
|
||||
`.android-public` and emits `dist-android`. The development server continues to
|
||||
use the source `public` tree so asset-pipeline work remains convenient. The
|
||||
complete source catalog and published immutable objects stay on TrueNAS, not in
|
||||
every player's initial browser or Android installation.
|
||||
|
||||
## Content manifest
|
||||
|
||||
@@ -56,8 +56,9 @@ The runtime continues to request stable paths such as:
|
||||
```
|
||||
|
||||
The content resolver maps that path to a verified file in Android app-private
|
||||
storage. Filenames are therefore not used as version numbers and runtime code
|
||||
does not need to know the hash.
|
||||
storage or, in production browsers, to the app-managed content cache through a
|
||||
same-origin service worker. Filenames are therefore not used as version numbers
|
||||
and runtime code does not need to know the hash.
|
||||
|
||||
## Safe publication and activation
|
||||
|
||||
@@ -68,17 +69,36 @@ Publication order is intentionally atomic:
|
||||
3. Write the versioned manifest.
|
||||
4. Replace `manifest.json` only after all referenced objects exist.
|
||||
|
||||
On a device, the client fetches the small manifest and compares it with its
|
||||
installed manifest. Only missing or hash-mismatched objects are downloaded.
|
||||
Android streams each object to a `.part` file, supports HTTP range resume,
|
||||
verifies its size and SHA-256, then renames it into the object cache. The active
|
||||
logical-path map changes only after the complete pack verifies. An interrupted
|
||||
or corrupt update leaves the previous pack playable.
|
||||
The client fetches the small manifest and compares it with its bundled and
|
||||
installed file identities. Only missing or hash-mismatched objects are
|
||||
downloaded. Browsers download into a temporary Cache Storage staging area,
|
||||
verify every object's byte count and SHA-256, and promote the pack to the active
|
||||
cache only after the complete pack succeeds. Android streams each object to a
|
||||
`.part` file, supports HTTP range resume, verifies its size and SHA-256, then
|
||||
renames it into the object cache. An interrupted or corrupt update leaves the
|
||||
previous pack playable on either platform.
|
||||
|
||||
The starter pack is bundled into the APK. Downloaded packs live under Android's
|
||||
app-private files directory, outside the packaged web bundle, and survive an
|
||||
APK update. Players may remove optional packs through the content manager; save
|
||||
data is stored separately and is never removed with content.
|
||||
The Downloads screen uses the same lifecycle in browser and Android. A missing
|
||||
pack offers **Download**, an installed pack with changed files offers
|
||||
**Update**, and an installed optional pack offers **Remove**. Required packs are
|
||||
marked as included and cannot be removed. Browser packs live in the app-managed
|
||||
cache; Android packs live in app-private storage and survive APK updates. Save
|
||||
data and roster identities are stored separately and are never removed with
|
||||
content.
|
||||
|
||||
Character packs are presentation-only. When a selected race's pack is absent,
|
||||
removed, disabled, or fails to load, the shared character renderer uses the
|
||||
matching-gender basic Human model and default appearance. The saved race, class,
|
||||
equipment, progression, and appearance choices remain intact and become visible
|
||||
again after the pack is installed.
|
||||
|
||||
Original spell effects and authentic sounds are enabled by default. The
|
||||
`core:combat-media` pack contains the original models and textures for every
|
||||
mapped WoW class visual, plus authentic sounds. It downloads automatically
|
||||
when enabled and stays outside required bootstrap content. Missing visual
|
||||
phases use generated effects; missing sounds stay silent. Removing the pack
|
||||
returns visual playback to the generated fallback. Run `npm run effects:wow`
|
||||
before the normal content publishing workflow to regenerate the original assets.
|
||||
|
||||
## Offline behavior
|
||||
|
||||
@@ -87,6 +107,8 @@ data is stored separately and is never removed with content.
|
||||
- The last verified content map is read synchronously from local storage.
|
||||
- A failed update check is silent outside the content manager.
|
||||
- A failed or interrupted download never replaces active content.
|
||||
- Missing optional character packs fall back to the basic Human model.
|
||||
- Generated spell effects remain available without original spell media.
|
||||
- Local saves are authoritative during play and cloud synchronization retries
|
||||
when connectivity returns.
|
||||
- Content that has never been downloaded is clearly marked as unavailable
|
||||
@@ -104,9 +126,10 @@ Keeping the runtime in the APK is what makes offline startup reliable.
|
||||
/mnt/usbssds/apps/healer-man/backups Database backups
|
||||
```
|
||||
|
||||
The Node server exposes the content volume at `/content`. Manifests use
|
||||
`no-cache`; immutable hashed objects use long-lived cache headers and support
|
||||
byte ranges for resumed downloads.
|
||||
The Node server exposes the compact production browser client at `/` and the
|
||||
complete downloadable-content volume at `/content`. Manifests use `no-cache`;
|
||||
immutable hashed objects use long-lived cache headers and support byte ranges
|
||||
for resumed downloads.
|
||||
|
||||
After pulling source on TrueNAS, publish content before restarting the app:
|
||||
|
||||
@@ -121,19 +144,36 @@ discover a manifest whose files have not been published.
|
||||
## Pack policy
|
||||
|
||||
Packs are player-facing groups while files remain individually addressable.
|
||||
This permits a fast initial pack install and small later patches. Current pack
|
||||
IDs are derived predictably:
|
||||
This permits a compact initial install and small later patches. Required pack
|
||||
IDs include:
|
||||
|
||||
```text
|
||||
core:runtime
|
||||
core:ui
|
||||
core:equipment
|
||||
core:character-basic
|
||||
dungeon:wailing-caverns
|
||||
```
|
||||
|
||||
`core:character-basic` owns the two basic Human rigs, the character manifest,
|
||||
and only the default texture selections needed by the fallback. The remaining
|
||||
human customizations and every non-Human character model/skin are optional.
|
||||
|
||||
Downloadable pack IDs include:
|
||||
|
||||
```text
|
||||
dungeon:forsaken-abbey
|
||||
characters:wow335a-v1
|
||||
characters:ascension-v1
|
||||
characters:runewaker-v1
|
||||
manastorm:<stage-id>
|
||||
core:combat-media
|
||||
shared:creatures
|
||||
```
|
||||
|
||||
`manastorm:*` and `core:arenas` remain valid internal pack IDs for compatibility
|
||||
but are hidden from the Downloads screen. Installing a regular dungeon still
|
||||
installs its exact environment dependency closure automatically.
|
||||
|
||||
Changing one GLB changes one object hash. The client downloads that object and
|
||||
atomically updates the pack rather than downloading every dungeon again.
|
||||
|
||||
|
||||
+33
-19
@@ -24,8 +24,9 @@ container, rebuilding `dist`, or pulling source must not replace the database.
|
||||
TrueNAS Gitea stores the source repository and APK release attachments; it is
|
||||
not the game database.
|
||||
|
||||
Large runtime assets are published separately under `/app/content`. Android
|
||||
clients download verified content objects from `/content` and retain them in
|
||||
Large runtime assets are published separately under `/app/content`. Production
|
||||
browser and Android clients download verified content objects from `/content`;
|
||||
browsers retain them in an app-managed cache and Android retains them in
|
||||
app-private storage for offline play. See `CONTENT_UPDATES.md` for the manifest,
|
||||
atomic activation, compatibility, and rollback contract.
|
||||
|
||||
@@ -55,9 +56,10 @@ Container/host port:
|
||||
```
|
||||
|
||||
The Gitea bare repository is not runnable. Clone it with Git; never copy it
|
||||
with `cp`. The checkout contains several gigabytes of required GLB assets, so
|
||||
the initial clone and first build will take substantially longer than later
|
||||
fast-forward updates.
|
||||
with `cp`. The checkout contains several gigabytes of content-source GLB assets,
|
||||
so the initial clone and first content publication will take substantially
|
||||
longer than later fast-forward updates. Production browser and Android bundles
|
||||
stage only their compact required-content subset.
|
||||
|
||||
Do not turn the GLBs into Git LFS pointers without also changing this deployment
|
||||
procedure. Gitea keeps LFS objects outside the bare repository, so a bare-path
|
||||
@@ -80,7 +82,8 @@ git branch --show-current
|
||||
|
||||
If the repository slug differs, change both the remote URL here and every
|
||||
`healer-man.git` bare-repository path below. Do not commit `.env` files,
|
||||
keystores, API tokens, `content`, `dist`, `dist-android`, or `.android-public`.
|
||||
keystores, API tokens, `content`, `dist`, `dist-android`, `.web-public`, or
|
||||
`.android-public`.
|
||||
|
||||
On TrueNAS, confirm the bare repository path. If the first command fails, use
|
||||
the search command:
|
||||
@@ -132,6 +135,7 @@ services:
|
||||
CONTENT_DIR: /app/content
|
||||
DATA_DIR: /app/runtime-data
|
||||
HOST: 0.0.0.0
|
||||
HEALER_MAN_ENABLE_SHELVED_MODES: "0"
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
PORT: "4173"
|
||||
SESSION_TTL_DAYS: "30"
|
||||
@@ -154,8 +158,9 @@ account/cloud-save data; `/app/content` owns downloadable manifests and
|
||||
immutable content objects. Never mount a volume at `/app/data`, because that
|
||||
would hide the checkout's required `/app/data/loot/schema.sql`. The
|
||||
startup command installs locked dependencies, initializes the schema, publishes
|
||||
new or changed content objects, builds the full browser bundle, and starts the
|
||||
combined static/API/content server. Existing content objects are reused.
|
||||
new or changed content objects, builds the compact required-content browser
|
||||
bundle, and starts the combined static/API/content server. Existing content
|
||||
objects are reused.
|
||||
|
||||
After startup, test on the LAN:
|
||||
|
||||
@@ -185,6 +190,7 @@ on the development PC. Stop if any test or build fails:
|
||||
|
||||
```powershell
|
||||
Set-Location F:\Projects\HealerMan
|
||||
npm run release:check
|
||||
|
||||
$VersionName = '0.1.3'
|
||||
$VersionCode = 1003
|
||||
@@ -202,12 +208,13 @@ $ReleaseVersion = [ordered]@{
|
||||
npm run content:test
|
||||
npm run server:test
|
||||
npm test
|
||||
npm run content:publish
|
||||
npm run build
|
||||
npm test -- --run src/avatar
|
||||
npx vitest run src/game/inputManager.test.ts src/game/inputMath.test.ts
|
||||
|
||||
npm run release:check
|
||||
git status --short
|
||||
git diff --check
|
||||
git add -A
|
||||
git status --short
|
||||
git diff --cached --stat
|
||||
@@ -217,10 +224,15 @@ git push origin main
|
||||
git rev-parse HEAD
|
||||
```
|
||||
|
||||
`content:publish` is a fail-closed pre-build release check. It must report all
|
||||
54 WoW campaign dungeon packs and refuses to replace the manifest when a pack,
|
||||
dependency, or catalog-referenced asset is missing. Its local `content/` output
|
||||
is ignored and must not be staged.
|
||||
|
||||
Before `git commit`, read the staged file list printed by `git status --short`.
|
||||
Do not commit a `.env` file, password, API token, keystore, APK, database,
|
||||
`content/`, `dist/`, `dist-android/`, or `.android-public/` output. The final
|
||||
command prints the exact deployed commit ID.
|
||||
`content/`, `dist/`, `dist-android/`, `.web-public/`, or `.android-public/`
|
||||
output. The final command prints the exact deployed commit ID.
|
||||
|
||||
Next, open the TrueNAS Apps UI and **stop** `healer-man`. Open a TrueNAS shell
|
||||
and run these exact commands:
|
||||
@@ -246,11 +258,12 @@ sudo git -C /mnt/usbssds/apps/healer-man/app rev-parse HEAD
|
||||
The TrueNAS commit ID must match the one printed on the development PC. Start
|
||||
`healer-man` in the TrueNAS Apps UI afterward. Container startup
|
||||
publishes immutable content objects and replaces `manifest.json` only after all
|
||||
objects exist, then rebuilds the browser client and starts the server. Android
|
||||
devices discover the small new manifest and download only missing or changed
|
||||
files. Asset-only updates do not require a new APK. A failed fast-forward pull
|
||||
deliberately leaves the running checkout unchanged; resolve diverged history on
|
||||
the development machine rather than forcing the TrueNAS checkout.
|
||||
objects exist, then rebuilds the browser client and starts the server. Browser
|
||||
and Android clients discover the small new manifest and download only missing
|
||||
or changed files for packs the player installed. Asset-only updates do not
|
||||
require a new APK. A failed fast-forward pull deliberately leaves the running
|
||||
checkout unchanged; resolve diverged history on the development machine rather
|
||||
than forcing the TrueNAS checkout.
|
||||
|
||||
After the logs say the server is listening on port `4173`, run on TrueNAS:
|
||||
|
||||
@@ -285,9 +298,10 @@ copies; it does not replace dataset-level protection.
|
||||
## APK releases and Obtainium
|
||||
|
||||
Keep APK binaries out of the Git branch. The Android build uses `dist-android`,
|
||||
which contains the game runtime, UI, equipment, and Wailing Caverns starter
|
||||
content rather than the full multi-gigabyte browser catalog. Downloaded packs
|
||||
survive APK updates.
|
||||
which contains the game runtime, UI, starter equipment, basic Human character,
|
||||
and Wailing Caverns starter content. Optional character/skin packs, original
|
||||
spell media, dungeons, and other large content remain outside the APK.
|
||||
Downloaded packs survive APK updates.
|
||||
|
||||
Install JDK 21 and Android SDK Platform 36 on the development PC and set
|
||||
`JAVA_HOME` plus `ANDROID_SDK_ROOT`. The build wrapper also recognizes the
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# Encounter authoring and fidelity
|
||||
|
||||
Healer Man keeps boss abilities, dungeon orchestration, presentation, and source
|
||||
evidence separate. Boss abilities are modular `EnemyAttackDefinition` records;
|
||||
dungeon-wide doors, waves, checkpoints, and transitions are modular
|
||||
`DungeonEncounterDefinition` records. Neither runtime stores Three.js or React
|
||||
objects, so the same definitions can later be selected or randomized without
|
||||
rewriting a boss component.
|
||||
|
||||
## Adding or editing a boss mechanic
|
||||
|
||||
1. Add or edit the attack in the boss entity's `combat.attacks` list. Imported
|
||||
RuneWaker lists are generated under `src/game/generated`; authored WoW
|
||||
exceptions live with their dungeon content.
|
||||
2. Give the mechanic a stable `id`, timing, condition, target, effect, and source
|
||||
record. Health mechanics use `self-health-percent`; timed mechanics use
|
||||
`initialCooldownMs` and `repeatCooldownMs`.
|
||||
3. Author a `telegraph` when the source supplies geometry. Harmful area attacks
|
||||
without explicit geometry receive a conservative circle/cone/line inference,
|
||||
but inferred geometry is not described as source-exact.
|
||||
4. Add a guide override only when the generated explanation needs encounter
|
||||
context that cannot be derived from the mechanic. The Boss Guide otherwise
|
||||
reads the runtime definition directly, including its trigger, timer, effect,
|
||||
telegraph, counterplay, fidelity, and test procedure.
|
||||
5. Run the focused mechanic, telegraph-geometry, campaign, timeline, and guide
|
||||
tests, then perform the browser playtest described in `PLAYTEST_NOTES.md`.
|
||||
|
||||
Telegraph geometry is shared by presentation and hit resolution. A circle,
|
||||
frontal cone, or line shown on the floor is therefore the area the resolver
|
||||
tests when the cast completes. `warningMs` is also enforced as a minimum cast
|
||||
time, so a telegraph cannot appear after its damage resolves.
|
||||
|
||||
## Adding a dungeon encounter rule
|
||||
|
||||
Attach a `DungeonEncounterDefinition` to `DungeonDefinition.encounters`. Rules
|
||||
combine a trigger, optional guards, and reusable actions. Available primitives
|
||||
cover tick/elapsed time, signals, interactions, entity engagement/death/health,
|
||||
all-members-dead checks, phases, flags, counters, timers, spawn groups, dynamic
|
||||
world-object state, objective completion, checkpoints, party teleports, loot,
|
||||
and signal chaining.
|
||||
|
||||
Definitions and snapshots are JSON-safe and stable-ID based. Actions are
|
||||
idempotent by default. Loot actions must use `persistence: "run"`; this prevents
|
||||
a wipe or checkpoint reload from duplicating a reward. Declare encounter-owned
|
||||
mob waves in `DungeonDefinition.encounterSpawnGroups`. Declare doors, switches,
|
||||
chests, keys, bridges, NPC prompts, and quest objects in
|
||||
`DungeonDefinition.serverGameObjects`; their collision, sight blocking, visual,
|
||||
prompt, and allowed interaction states follow the current encounter snapshot.
|
||||
|
||||
Online groups use party-leader authority. A follower interaction is sent as a
|
||||
request, then revalidated by the leader against the follower's synchronized
|
||||
position, range, line of sight, object state, and object ID before any rule is
|
||||
dispatched. The leader publishes serializable encounter snapshots to followers.
|
||||
|
||||
## Save behavior
|
||||
|
||||
Character profiles store a versioned safe dungeon checkpoint. The checkpoint
|
||||
projection retains only actions marked `checkpoint` or `run`; active timers,
|
||||
temporary waves, and a half-finished pull are reset. Entering the same dungeon
|
||||
hydrates that safe projection and its spawn after definitions are registered.
|
||||
Loading a different dungeon starts at its normal entrance.
|
||||
|
||||
## Source fidelity and remaining review
|
||||
|
||||
The deterministic ledgers are:
|
||||
|
||||
- `scripts/runewaker-pipeline/RUNEWAKER_ENCOUNTER_IMPLEMENTATION_REPORT.md`
|
||||
- `scripts/runewaker-pipeline/RUNEWAKER_GAMEPLAY_CONTENT_REPORT.md`
|
||||
- `scripts/runewaker-pipeline/RUNEWAKER_ENVIRONMENT_ASSEMBLY_REPORT.md`
|
||||
- `scripts/runewaker-pipeline/RUNEWAKER_NAVIGATION_GAP_REPORT.md`
|
||||
|
||||
Current encounter evidence covers 30 RuneWaker packages, 4,113 deferred source
|
||||
rows, and 1,524 unique direct evidence records. Of those evidence records, 388
|
||||
have exact executable spell mappings and 1,136 still need source review. Four
|
||||
bosses have clearly labeled, playable Healer Man fallback kits because the
|
||||
preserved data does not expose their original executable behavior. Eight
|
||||
non-empty packages do not contain source-confirmed boss membership; two
|
||||
authoritative variants are intentionally empty.
|
||||
|
||||
The upgraded WDB environment builder can assemble all referenced ROS resources
|
||||
with full placement translation, quaternion, and nonuniform scale, source-hash
|
||||
caching, shared meshes, collision chunking, and a separate dynamic-placement
|
||||
manifest. The integration pass regenerated Raksha Temple and The Origin through
|
||||
that full-WDB path; remaining shipping packages retain their last validated
|
||||
build until they are individually regenerated. The current audit finds 107
|
||||
filename-only dynamic candidates; they remain static and review-required
|
||||
because filenames alone do not prove door/switch behavior.
|
||||
|
||||
Treasure Trove's 908-placement full-WDB staging export is reproducible and its
|
||||
487,380-triangle visual/collision assembly, meshopt pass, and KTX2 compression
|
||||
all succeed. Its 0.3-metre, 512-cell full-resolution Recast navigation bake did
|
||||
not complete: the watershed run was stopped after nearly two CPU-hours when it
|
||||
showed the smaller-tile retry signature, and the explicit monotone run was
|
||||
stopped after more than four CPU-hours on its first pass. The fail-closed
|
||||
pipeline therefore did not publish that staged package. The last validated
|
||||
shipping package remains active. The recipe retains the monotone tiled policy
|
||||
for a future offline bake or a reviewed spatial-partitioning optimization;
|
||||
lowering navigation resolution was not silently substituted.
|
||||
|
||||
The route report contains 19 unreachable legs across eight dungeons, all from a
|
||||
distance-from-entrance objective-order heuristic. Those legs are evidence that
|
||||
objective order needs review, not proof that an off-mesh link should exist.
|
||||
Links are added only when source geometry or a reviewed traversal justifies
|
||||
them.
|
||||
|
||||
Run `npm run runewaker:audit:gameplay`, `npm run
|
||||
runewaker:audit:environment-assembly`, and `npm run runewaker:audit:navigation`
|
||||
after importing new source data. A report can be structurally green while its
|
||||
content status remains `review-required`; this is intentional and prevents an
|
||||
incomplete source inference from being presented as an original mechanic.
|
||||
+1695
File diff suppressed because it is too large.
Load diff
@@ -41,6 +41,42 @@ npm test
|
||||
npm run build
|
||||
```
|
||||
|
||||
Manastorms, Arenas/PvP, and their GM tools are shelved in normal browser and
|
||||
Android builds. Their source and saved data remain intact. Use
|
||||
`npm run dev:shelved` or `npm run build:shelved` for maintenance builds, and
|
||||
set `HEALER_MAN_ENABLE_SHELVED_MODES=1` on the matching development server.
|
||||
|
||||
Boss-mechanic and dungeon-event extension points, telegraph rules, checkpoint
|
||||
behavior, test workflow, and the explicit source-gap ledger are documented in
|
||||
[`ENCOUNTER_AUTHORING.md`](ENCOUNTER_AUTHORING.md).
|
||||
|
||||
## Content downloads
|
||||
|
||||
Production browser and Android builds ship a compact required set: the game
|
||||
runtime, interface, starter equipment, Wailing Caverns starter content, and a
|
||||
basic male/female Human character model with its default appearance. Large
|
||||
character and skin libraries, additional dungeons, and the original
|
||||
spell-effects and sound pack are optional downloads. Manastorm environment
|
||||
packs remain internal dungeon dependencies and are installed automatically
|
||||
when a regular dungeon needs one; they are not shown as standalone downloads.
|
||||
|
||||
Open **Downloads** from the main menu to download, update, or remove optional
|
||||
packs on either platform. Browser downloads live in an app-managed verified
|
||||
cache; Android downloads live in app-private storage. Removing a pack never
|
||||
removes a roster or save. If a character's model/skin pack is unavailable, the
|
||||
shared character renderer displays the matching-gender basic Human while
|
||||
preserving that character's identity and progress for a later reinstall.
|
||||
|
||||
Original spell effects and authentic sounds are enabled by default; their
|
||||
optional media pack downloads automatically. All ten WoW classes use packaged
|
||||
source visuals, including animated meshes, particles, ribbon trails, and
|
||||
particle model dependencies. Generated visuals cover unavailable phases;
|
||||
missing sounds stay silent. The pack can be removed to reclaim storage. See
|
||||
[`scripts/combat-effects/README.md`](scripts/combat-effects/README.md) for coverage,
|
||||
regeneration, and the local effect preview. See
|
||||
[`CONTENT_UPDATES.md`](CONTENT_UPDATES.md) for the content lifecycle and release
|
||||
contract.
|
||||
|
||||
## Conquest of Azeroth classes
|
||||
|
||||
All 21 CoA classes use a checked-in live Vol'Jin catalog generated from
|
||||
@@ -81,12 +117,23 @@ validates the relational data and ships both a browser-safe JSON catalog and
|
||||
for a future loot-browser screen.
|
||||
|
||||
Every Wailing Caverns boss rolls one item when it dies. The item instance
|
||||
captures the player's level at the killing blow, remains on the corpse until
|
||||
looted, and is then saved in that character's inventory. The SQL loot source
|
||||
contains the original 3.3.5a attributes, ratings, armor, block, weapon damage,
|
||||
weapon speed, regeneration, and spell-power values for each item. A scaled
|
||||
drop preserves that original stat distribution instead of replacing it with a
|
||||
generic item-power budget.
|
||||
captures its item level at the killing blow, remains on the corpse until
|
||||
looted, and is then saved in that character's inventory. Drops use character
|
||||
level through 79; level-80 drops begin the first endgame tier at item level 85.
|
||||
The SQL loot source contains the original 3.3.5a attributes, ratings, armor,
|
||||
block, weapon damage, weapon speed, regeneration, and spell-power values for
|
||||
each item. A scaled drop preserves that original stat distribution instead of
|
||||
replacing it with a generic item-power budget.
|
||||
|
||||
Every WoW and Conquest of Azeroth character has a saved specialization that
|
||||
locks its activity role and selects a complete specialization-aware heirloom
|
||||
loadout. Runes of Magic derives the same profile from its active primary class
|
||||
and selected role. Virtual heirlooms fill every applicable unoccupied slot,
|
||||
rescale through item level 79 for players and 80 for AI party members, never
|
||||
enter inventory, and automatically return when earned equipment is removed.
|
||||
During leveling their score is normalized above compatible same-level catalog
|
||||
drops. At level 80 the item-level-85 boss tier contains real upgrades, which
|
||||
the equipment and loot interfaces highlight without equipping automatically.
|
||||
|
||||
The equipment runtime uses the Wrath primary attributes and itemizable combat
|
||||
stats. Strength, Agility, Stamina, Intellect, Spirit, Attack Power, Spell
|
||||
@@ -181,12 +228,13 @@ npm run android:apk
|
||||
The debug APK is written under `android/app/build/outputs/apk/debug/`. To build
|
||||
and install directly onto a connected device, use `npm run android:install`.
|
||||
|
||||
Android uses a separate compact `dist-android` build. It bundles the client,
|
||||
interface, equipment, and Wailing Caverns starter content while optional
|
||||
dungeons, character packs, and Manastorm stages are downloaded into app-private
|
||||
storage. Installed content and local saves remain playable without a server
|
||||
connection and survive APK updates. See `CONTENT_UPDATES.md` for the update
|
||||
contract and `DEPLOYMENT.md` for TrueNAS publishing and signed Gitea releases.
|
||||
Android uses a compact `dist-android` build with the same required-content
|
||||
boundary as the production browser build. Optional dungeons, character/skin
|
||||
packs, internal environment dependencies, and original spell media are
|
||||
downloaded into app-private storage. Installed content and local saves remain playable without
|
||||
a server connection and survive APK updates. See `CONTENT_UPDATES.md` for the
|
||||
update contract and `DEPLOYMENT.md` for TrueNAS publishing and signed Gitea
|
||||
releases.
|
||||
|
||||
## Runtime details
|
||||
|
||||
@@ -206,6 +254,8 @@ contract and `DEPLOYMENT.md` for TrueNAS publishing and signed Gitea releases.
|
||||
compressed in GPU memory instead of expanding to uncompressed RGBA textures.
|
||||
- Missing production GLBs activate a small cave proxy; malformed assets activate
|
||||
the recoverable scene-error screen.
|
||||
- Missing optional character or skin packs render the built-in basic Human
|
||||
model instead of hiding the player, party member, or roster preview.
|
||||
- The controller action HUD displays only the active eight-slot layer. Its
|
||||
face-button diamond, shoulders, and D-pad keep the physical controller layout
|
||||
visible without showing all 24 bindings over the playfield.
|
||||
|
||||
+36
-7
@@ -18,6 +18,21 @@ approval before it commits, pushes `main`, builds the signed APK, and publishes
|
||||
the Gitea release. Signing passwords and the Gitea token are passed to the
|
||||
child process in memory rather than on its command line.
|
||||
|
||||
Before changing versions or running the longer test/build sequence, the manager
|
||||
runs an isolated release-candidate check. It validates tracked and untracked
|
||||
files without changing the real Git index, so extra blank lines at EOF and
|
||||
other whitespace errors fail immediately. Run the same fast check at any time:
|
||||
|
||||
```powershell
|
||||
npm run release:check
|
||||
```
|
||||
|
||||
Editors should honor the repository's `.editorconfig`. The checked-in VS Code
|
||||
settings also trim extra final newlines whenever a file is saved.
|
||||
Git may print a one-time CRLF-to-LF conversion warning when an existing Windows
|
||||
working copy is first staged under `.gitattributes`; that warning is not a
|
||||
release failure, and subsequent repository text stays consistently LF.
|
||||
|
||||
The local release key is `F:\secure\healer-man-release.jks`. When the companion
|
||||
`healer-man-release-password.dpapi` file is present, blank signing-password
|
||||
fields are filled from that Windows user-encrypted credential in memory. The
|
||||
@@ -56,9 +71,12 @@ file, use the shorter procedure at the end. Those updates can be published by
|
||||
the server and downloaded by an already-installed APK without an Obtainium
|
||||
release.
|
||||
|
||||
Single-player remains offline-capable. A fresh APK includes the runtime and
|
||||
starter content. Additional content that a player has downloaded is retained
|
||||
in Android app-private storage and can be used offline after download.
|
||||
Single-player remains offline-capable. A fresh APK includes the runtime,
|
||||
interface, starter equipment, Wailing Caverns, and the basic Human character
|
||||
fallback. Additional content that a player has downloaded is retained in
|
||||
Android app-private storage and can be used offline after download. Generated
|
||||
spell effects are available without downloading the optional original-media
|
||||
pack.
|
||||
|
||||
## Fixed locations
|
||||
|
||||
@@ -219,10 +237,15 @@ Gitea tag after the signed APK builds successfully.
|
||||
npm run content:test
|
||||
npm run server:test
|
||||
npm test
|
||||
npm run content:publish
|
||||
npm run build
|
||||
```
|
||||
|
||||
All four commands must succeed.
|
||||
All five commands must succeed. `content:publish` is a fail-closed release gate:
|
||||
it validates that the generated manifest contains all 54 WoW campaign dungeon
|
||||
packs, their exact dependency closure, and every catalog-referenced asset before
|
||||
it writes the ignored local `content/` output. Do not build or stage a release
|
||||
if that validation fails.
|
||||
|
||||
Apply these additional targeted checks whenever the matching area was edited:
|
||||
|
||||
@@ -254,11 +277,14 @@ npm run dev
|
||||
First check for whitespace errors and review the final file list:
|
||||
|
||||
```powershell
|
||||
git diff --check
|
||||
npm run release:check
|
||||
git status --short
|
||||
git diff --stat
|
||||
```
|
||||
|
||||
Use `npm run release:check` before the longer release validation. Unlike
|
||||
`git diff --check`, it includes new, untracked files.
|
||||
|
||||
Stage the intended release:
|
||||
|
||||
```powershell
|
||||
@@ -269,7 +295,8 @@ git diff --cached --check
|
||||
```
|
||||
|
||||
Do not continue if the staged list contains a keystore, APK, `.env` file,
|
||||
database, `content/`, `dist/`, `dist-android/`, or `.android-public/` output.
|
||||
database, `content/`, `dist/`, `dist-android/`, `.web-public/`, or
|
||||
`.android-public/` output.
|
||||
Unstage any accidental file with `git restore --staged -- <path>` without
|
||||
deleting the local file.
|
||||
|
||||
@@ -518,9 +545,10 @@ Do **not** bump the Android version. Run these exact PowerShell commands:
|
||||
```powershell
|
||||
Set-Location F:\Projects\HealerMan
|
||||
npm run content:test
|
||||
npm run content:publish
|
||||
npm run build
|
||||
git status --short
|
||||
git diff --check
|
||||
npm run release:check
|
||||
git add -A
|
||||
git status --short
|
||||
git diff --cached --stat
|
||||
@@ -629,6 +657,7 @@ services:
|
||||
CONTENT_DIR: /app/content
|
||||
DATA_DIR: /app/runtime-data
|
||||
HOST: 0.0.0.0
|
||||
HEALER_MAN_ENABLE_SHELVED_MODES: "0"
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
PORT: "4173"
|
||||
SESSION_TTL_DAYS: "30"
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# RuneWaker encounter content catalog
|
||||
|
||||
This is the reviewed runtime layer above the exhaustive, machine-generated RuneWaker
|
||||
encounter audit. It intentionally contains only source behavior that can be mapped without
|
||||
guessing. The executable catalog is `src/game/runewakerEncounterCatalog.ts`.
|
||||
|
||||
## Bounded reviewed coverage
|
||||
|
||||
| Dungeon | Deferred source rows mapped | Runtime behavior | Fidelity |
|
||||
| --- | --- | --- | --- |
|
||||
| Forsaken Abbey | `12230`, `12236`, `12238` | Three two-member Ghoul/Carrion Spider formations are created on encounter initialization. After the source `sleep(20)` (2 seconds because RuneWaker sleep units are tenths of a second), the encounter enters its patrol phase. | source-backed adapted |
|
||||
| Treasure Trove | `5952428`, `5952438` | Killing Boulder Sidaar (`5952494`) arms switch `112100`; interacting with it activates the switch and lowers drawbridge `112098` once. | source-backed adapted |
|
||||
|
||||
The Forsaken mapping is backed by preserved Lua `00571.lua` and matrix helper `00727.lua`.
|
||||
The Treasure Trove mapping is backed by preserved Lua `01159.lua`. Snapshot, mechanics-audit,
|
||||
and source hashes are attached to each catalog review record.
|
||||
|
||||
“Source-backed adapted” is deliberate. Source IDs, trigger order, spawn membership, timing,
|
||||
and placement transforms are retained. HealerMan supplies serializable phases, procedural
|
||||
world-object geometry, projected heights, interaction range, and the packaged canonical Ghoul
|
||||
visual/combat definition for the source-only level-scaled Ghoul template `100653`.
|
||||
|
||||
## Effective ledger after this review
|
||||
|
||||
The generated audit baseline inventories 4,113 deferred source rows and 1,136 unresolved
|
||||
mechanics-evidence records. This catalog maps five unique deferred rows and clears five stable
|
||||
evidence IDs, leaving:
|
||||
|
||||
| Ledger item | Baseline | Reviewed here | Remaining |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Deferred source rows | 4,113 | 5 | 4,108 |
|
||||
| Unresolved mechanics-evidence records | 1,136 | 5 | 1,131 |
|
||||
|
||||
Per affected package, Forsaken Abbey moves from 101 to 98 deferred rows and 77 to 74
|
||||
unresolved evidence records. Treasure Trove moves from 188 to 186 deferred rows and 47 to 45
|
||||
unresolved evidence records. All other package counts remain unchanged.
|
||||
|
||||
The exhaustive generated JSON remains the immutable audit baseline until its generator learns
|
||||
to consume the runtime catalog. The effective counts above are exported as
|
||||
`RUNEWAKER_ENCOUNTER_CONTENT_LEDGER` and are regression tested.
|
||||
|
||||
## Explicit remaining gaps
|
||||
|
||||
- Forsaken formations remain at their source start positions after the two-second hold. The
|
||||
source uses endless flag-path patrols; the encounter runtime has no reviewed path-following
|
||||
action yet.
|
||||
- Forsaken source template `100653` is a level-scaled Ghoul variant created only by Lua and has
|
||||
no packaged actor row. The runtime alias reuses canonical Ghoul `100072`; this is not claimed
|
||||
as an exact visual or formula mapping.
|
||||
- The Forsaken keeper gate (`7883`) remains deferred. Its four Necromage deaths source-summon
|
||||
Ghoul Drake `100073`, but that summon has neither a packaged entity nor a reviewed combat
|
||||
definition. Opening the gate without that fight would be false fidelity.
|
||||
- Black Mary's Hold Door (`5952433`) remains deferred because its source interaction consumes
|
||||
inventory item `203550`; encounter guards currently expose no inventory predicate.
|
||||
- Treasure Trove uses procedural bridge/switch proxies because source object meshes and ACT
|
||||
motions are not packaged.
|
||||
- The remaining 4,108 rows require the same source-by-source review. Bulk promotion from names,
|
||||
model paths, or regex categories is not evidence of executable behavior.
|
||||
|
||||
## Validation
|
||||
|
||||
`src/game/runewakerEncounterCatalog.test.ts` validates each encounter definition and requires
|
||||
every referenced runtime spawn, entity, encounter spawn group, world object state, and objective
|
||||
to exist. It also simulates both reviewed state machines and locks the ledger arithmetic.
|
||||
@@ -24,6 +24,8 @@ android {
|
||||
versionName releaseVersionName
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Dungeon objects already carry verified lossless gzip compression.
|
||||
noCompress 'gzip'
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
|
||||
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.phenomrom.healerman;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import com.getcapacitor.Bridge;
|
||||
import org.json.JSONObject;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/** Streams shared APK objects at their original logical URLs; no expanded disk copy. */
|
||||
final class BundledContentAssets {
|
||||
private final Bridge bridge;
|
||||
private final AssetManager assets;
|
||||
private JSONObject files;
|
||||
|
||||
BundledContentAssets(Bridge bridge) {
|
||||
this.bridge = bridge;
|
||||
this.assets = bridge.getContext().getAssets();
|
||||
}
|
||||
|
||||
private synchronized JSONObject files() throws Exception {
|
||||
if (files == null) {
|
||||
try (InputStream input = assets.open("public/content/bootstrap-manifest.json")) {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[16384];
|
||||
int count;
|
||||
while ((count = input.read(buffer)) != -1) output.write(buffer, 0, count);
|
||||
files = new JSONObject(output.toString("UTF-8")).getJSONObject("files");
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
WebResourceResponse intercept(WebResourceRequest request) {
|
||||
if (!"GET".equals(request.getMethod()) || !bridge.getHost().equals(request.getUrl().getAuthority())
|
||||
|| !bridge.getScheme().equals(request.getUrl().getScheme())) return null;
|
||||
String path = request.getUrl().getPath();
|
||||
if (path == null || !(path.startsWith("/assets/") || path.startsWith("/basis/") || path.startsWith("/data/"))) return null;
|
||||
try {
|
||||
JSONObject entry = files().optJSONObject(path);
|
||||
if (entry == null) return null;
|
||||
JSONObject download = entry.optJSONObject("download");
|
||||
String objectUrl = (download == null ? entry : download).getString("url");
|
||||
if (!objectUrl.matches("/content/bundled/[a-f0-9]{64}\\.[a-z0-9]+(?:\\.g(?:z|zip))?")) return null;
|
||||
InputStream input = assets.open("public" + objectUrl);
|
||||
if (download != null) {
|
||||
try { input = new GZIPInputStream(input, 128 * 1024); }
|
||||
catch (Exception error) { input.close(); throw error; }
|
||||
}
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Content-Length", String.valueOf(entry.getLong("bytes")));
|
||||
headers.put("Cache-Control", "public, max-age=3600");
|
||||
return new WebResourceResponse(mimeType(path), null, 200, "OK", headers, input);
|
||||
} catch (Exception error) {
|
||||
return new WebResourceResponse("text/plain", "UTF-8", 500, "Bundled asset unavailable", new HashMap<>(),
|
||||
new ByteArrayInputStream("Bundled asset unavailable.".getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
|
||||
private static String mimeType(String path) {
|
||||
String extension = path.substring(path.lastIndexOf('.') + 1);
|
||||
switch (extension) {
|
||||
case "glb": return "model/gltf-binary";
|
||||
case "json": return "application/json";
|
||||
case "js": return "text/javascript";
|
||||
case "wasm": return "application/wasm";
|
||||
case "ktx2": return "image/ktx2";
|
||||
case "png": return "image/png";
|
||||
case "webp": return "image/webp";
|
||||
case "avif": return "image/avif";
|
||||
case "svg": return "image/svg+xml";
|
||||
case "mp3": return "audio/mpeg";
|
||||
case "ogg": return "audio/ogg";
|
||||
case "wav": return "audio/wav";
|
||||
case "webm": return "video/webm";
|
||||
default: return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,10 @@ import com.getcapacitor.Plugin;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import com.getcapacitor.PluginMethod;
|
||||
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||
import com.getcapacitor.BridgeWebViewClient;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
@@ -20,6 +24,7 @@ import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Locale;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/** Streams verified, resumable content objects into Android app-private storage. */
|
||||
@CapacitorPlugin(name = "ContentStorage")
|
||||
@@ -27,6 +32,18 @@ public class ContentStoragePlugin extends Plugin {
|
||||
private static final int BUFFER_BYTES = 128 * 1024;
|
||||
private static final long PROGRESS_INTERVAL_MS = 200L;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
BundledContentAssets bundled = new BundledContentAssets(bridge);
|
||||
bridge.setWebViewClient(new BridgeWebViewClient(bridge) {
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
WebResourceResponse response = bundled.intercept(request);
|
||||
return response != null ? response : super.shouldInterceptRequest(view, request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void download(PluginCall call) {
|
||||
execute(() -> {
|
||||
@@ -36,6 +53,10 @@ public class ContentStoragePlugin extends Plugin {
|
||||
String sha256 = normalizedSha(requiredString(call, "sha256"));
|
||||
String extension = normalizedExtension(requiredString(call, "extension"));
|
||||
long expectedBytes = numericLong(call, "bytes", -1L);
|
||||
boolean compressed = "gzip".equals(call.getString("encoding"));
|
||||
String transferSha = compressed ? normalizedSha(requiredString(call, "downloadSha256")) : sha256;
|
||||
long transferBytes = compressed ? numericLong(call, "downloadBytes", -1L) : expectedBytes;
|
||||
if (expectedBytes < 0 || transferBytes < 0) throw new IllegalArgumentException("Missing content size.");
|
||||
validateRemoteUrl(url);
|
||||
|
||||
File destination = objectFile(sha256, extension);
|
||||
@@ -47,54 +68,89 @@ public class ContentStoragePlugin extends Plugin {
|
||||
throw new IllegalStateException("Unable to replace invalid cached content.");
|
||||
}
|
||||
|
||||
File partial = new File(destination.getParentFile(), destination.getName() + ".part");
|
||||
long existingBytes = partial.isFile() ? partial.length() : 0L;
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.setConnectTimeout(15_000);
|
||||
connection.setReadTimeout(45_000);
|
||||
connection.setRequestProperty("Accept-Encoding", "identity");
|
||||
if (existingBytes > 0L) connection.setRequestProperty("Range", "bytes=" + existingBytes + "-");
|
||||
int status = connection.getResponseCode();
|
||||
boolean resumed = existingBytes > 0L && status == HttpURLConnection.HTTP_PARTIAL;
|
||||
if (status != HttpURLConnection.HTTP_OK && status != HttpURLConnection.HTTP_PARTIAL) {
|
||||
connection.disconnect();
|
||||
throw new IllegalStateException("Content server returned HTTP " + status + ".");
|
||||
}
|
||||
if (!resumed) existingBytes = 0L;
|
||||
long downloadedBytes = existingBytes;
|
||||
long totalBytes = expectedBytes > 0L
|
||||
? expectedBytes
|
||||
: existingBytes + Math.max(0L, connection.getContentLengthLong());
|
||||
long lastProgressAt = 0L;
|
||||
|
||||
try (
|
||||
InputStream input = new BufferedInputStream(connection.getInputStream(), BUFFER_BYTES);
|
||||
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(partial, resumed), BUFFER_BYTES)
|
||||
) {
|
||||
byte[] buffer = new byte[BUFFER_BYTES];
|
||||
int count;
|
||||
while ((count = input.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, count);
|
||||
downloadedBytes += count;
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastProgressAt >= PROGRESS_INTERVAL_MS) {
|
||||
notifyProgress(logicalPath, downloadedBytes, totalBytes);
|
||||
lastProgressAt = now;
|
||||
File partial = new File(destination.getParentFile(), destination.getName() + (compressed ? "." + transferSha + ".gz" : "") + ".part");
|
||||
if (!verifies(partial, transferSha, transferBytes)) {
|
||||
if (partial.isFile() && partial.length() >= transferBytes && !partial.delete()) {
|
||||
throw new IllegalStateException("Unable to reset an invalid partial download.");
|
||||
}
|
||||
long existingBytes = partial.isFile() ? partial.length() : 0L;
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.setConnectTimeout(15_000);
|
||||
connection.setReadTimeout(45_000);
|
||||
connection.setRequestProperty("Accept-Encoding", "identity");
|
||||
if (existingBytes > 0L) connection.setRequestProperty("Range", "bytes=" + existingBytes + "-");
|
||||
int status = connection.getResponseCode();
|
||||
boolean resumed = existingBytes > 0L && status == HttpURLConnection.HTTP_PARTIAL;
|
||||
if (status != HttpURLConnection.HTTP_OK && status != HttpURLConnection.HTTP_PARTIAL) {
|
||||
connection.disconnect();
|
||||
throw new IllegalStateException("Content server returned HTTP " + status + ".");
|
||||
}
|
||||
if (status == HttpURLConnection.HTTP_PARTIAL) {
|
||||
String range = connection.getHeaderField("Content-Range");
|
||||
if (!resumed || range == null || !range.startsWith("bytes " + existingBytes + "-")) {
|
||||
connection.disconnect();
|
||||
partial.delete();
|
||||
throw new IllegalStateException("Content server returned an invalid resume range.");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
notifyProgress(logicalPath, downloadedBytes, totalBytes);
|
||||
if (!resumed) existingBytes = 0L;
|
||||
long downloadedBytes = existingBytes;
|
||||
long totalBytes = transferBytes > 0L
|
||||
? transferBytes
|
||||
: existingBytes + Math.max(0L, connection.getContentLengthLong());
|
||||
long lastProgressAt = 0L;
|
||||
|
||||
if (!verifies(partial, sha256, expectedBytes)) {
|
||||
try (
|
||||
InputStream input = new BufferedInputStream(connection.getInputStream(), BUFFER_BYTES);
|
||||
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(partial, resumed), BUFFER_BYTES)
|
||||
) {
|
||||
byte[] buffer = new byte[BUFFER_BYTES];
|
||||
int count;
|
||||
while ((count = input.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, count);
|
||||
downloadedBytes += count;
|
||||
if (downloadedBytes > transferBytes) throw new IllegalStateException("Downloaded content exceeds expected size.");
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastProgressAt >= PROGRESS_INTERVAL_MS) {
|
||||
notifyProgress(logicalPath, downloadedBytes, totalBytes);
|
||||
lastProgressAt = now;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
notifyProgress(logicalPath, downloadedBytes, totalBytes);
|
||||
}
|
||||
|
||||
if (!verifies(partial, transferSha, transferBytes)) {
|
||||
partial.delete();
|
||||
throw new IllegalStateException("Downloaded content failed SHA-256 or size verification.");
|
||||
}
|
||||
File verified = partial;
|
||||
if (compressed) {
|
||||
verified = new File(destination.getParentFile(), destination.getName() + ".decoded");
|
||||
try (InputStream input = new GZIPInputStream(new FileInputStream(partial), BUFFER_BYTES);
|
||||
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(verified), BUFFER_BYTES)) {
|
||||
byte[] buffer = new byte[BUFFER_BYTES];
|
||||
long decodedBytes = 0;
|
||||
int count;
|
||||
while ((count = input.read(buffer)) != -1) {
|
||||
decodedBytes += count;
|
||||
if (decodedBytes > expectedBytes) throw new IllegalStateException("Expanded content exceeds expected size.");
|
||||
output.write(buffer, 0, count);
|
||||
}
|
||||
} catch (Exception error) { verified.delete(); throw error; }
|
||||
if (!verifies(verified, sha256, expectedBytes)) {
|
||||
verified.delete();
|
||||
partial.delete();
|
||||
throw new IllegalStateException("Expanded content failed SHA-256 or size verification.");
|
||||
}
|
||||
partial.delete();
|
||||
}
|
||||
if (destination.exists() && !destination.delete()) {
|
||||
throw new IllegalStateException("Unable to activate downloaded content.");
|
||||
}
|
||||
if (!partial.renameTo(destination)) {
|
||||
if (!verified.renameTo(destination)) {
|
||||
throw new IllegalStateException("Unable to atomically activate downloaded content.");
|
||||
}
|
||||
call.resolve(fileResult(destination, false));
|
||||
|
||||
@@ -100,6 +100,8 @@ public abstract class ControllerBridgeActivity extends BridgeActivity {
|
||||
addAxisTokens(currentTokens, event.getAxisValue(MotionEvent.AXIS_HAT_Y), "Button12", "Button13");
|
||||
addAxisTokens(currentTokens, leftStickX, "Axis0-", "Axis0+");
|
||||
addAxisTokens(currentTokens, leftStickY, "Axis1-", "Axis1+");
|
||||
addAxisTokens(currentTokens, rightStickX, "Axis2-", "Axis2+");
|
||||
addAxisTokens(currentTokens, rightStickY, "Axis3-", "Axis3+");
|
||||
addPositiveAxisToken(currentTokens, positiveControllerAxisValue(event, MotionEvent.AXIS_LTRIGGER, MotionEvent.AXIS_BRAKE), "Button6");
|
||||
addPositiveAxisToken(currentTokens, positiveControllerAxisValue(event, MotionEvent.AXIS_RTRIGGER, MotionEvent.AXIS_GAS), "Button7");
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@ public class ThorDualScreenPlugin extends Plugin implements DisplayManager.Displ
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
return bridge.getLocalServer().shouldInterceptRequest(request);
|
||||
return bridge.getWebViewClient().shouldInterceptRequest(view, request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# WoW dungeon atlas
|
||||
|
||||
Press **M** in a loaded WoW dungeon to open its original game map. The atlas
|
||||
shows a white player marker with camera direction, a green entrance, and gold
|
||||
numbered encounter markers. Patrol routes are optional. The minimap and companion
|
||||
map use the same artwork and coordinate projection.
|
||||
|
||||
Multi-floor dungeons have a floor selector. Auto chooses the smallest containing
|
||||
map rectangle; it is a horizontal best fit, not the WoW client's WMO-group floor
|
||||
detection. Select a floor manually when stacked rooms overlap. Coordinates outside
|
||||
the selected floor are not clamped to a misleading marker at the map edge.
|
||||
|
||||
The current import includes 51 standard WoW dungeons and 107 unique map images. The
|
||||
custom Tinkertech Showdown, Frozen Reach, and Forgotten Mine have no matching map
|
||||
records in the supplied archives and retain their survey maps. RuneWaker maps are
|
||||
unchanged. Missing images also fall back to the survey projection.
|
||||
|
||||
## Reimporting
|
||||
|
||||
Run `npm run dungeon:maps:import` from the HealerMan project root. An optional
|
||||
archive argument replaces the default `../downloads/patch-enUS-M.MPQ`.
|
||||
`WOW_CLIENT_ROOT` supplies the client archives for original Wrath map tiles;
|
||||
`WOW_DUNGEON_WORK_ROOT` supplies runtime drafts for alignment checks. Defaults
|
||||
are `../wow335a` and `../HealerMan-Storage/pipeline-work/dungeons`.
|
||||
|
||||
The importer reads the patch and client archives without modifying them. It
|
||||
decodes BLP2 tiles into cropped 1002x668 PNGs, reads WorldMapArea/DungeonMap bounds,
|
||||
and inverts the reviewed scene coordinate mapper, including shifted ADT packages
|
||||
and rotated global WMOs. It verifies alignment against imported creature spawns
|
||||
or the source entrance for an event-driven arena without static actors.
|
||||
Wailing Caverns retains its authored coordinate convention.
|
||||
|
||||
Images live in `public/assets/game/dungeons/<id>/maps/`, so existing dungeon
|
||||
content-pack rules include them. Runtime metadata is generated in
|
||||
`src/game/generated/wowDungeonMaps.generated.json`; provenance and coverage are in
|
||||
`dungeon-pipeline/WOW_DUNGEON_MAP_REPORT.json`. Changes to environment checksums or
|
||||
coordinate conventions require reimporting. Mapster is a Lua WoW-client addon;
|
||||
HealerMan does not execute or install it.
|
||||
|
||||
After reimporting, run `npm run content:publish` to update the local downloadable
|
||||
content catalog and `npm run build` to refresh the bundled Wailing Caverns atlas.
|
||||
Those commands prepare local artifacts; they do not deploy the online game.
|
||||
|
||||
## Verification
|
||||
|
||||
Run `npm test -- --run src/game/wowDungeonMap.test.ts src/ui/DungeonMapGraphic.test.ts src/ui/MapPanel.test.ts`
|
||||
and `node --test scripts/lib/wow-map-image.test.mjs`. Visual evidence and observed
|
||||
limitations are recorded in `PLAYTEST_NOTES.md`.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Lossless game content packaging
|
||||
|
||||
`npm run web:prepare` and `npm run android:prepare` include exactly five packs:
|
||||
`core:ui`, `core:equipment`, `core:character-basic`, `core:runtime`, and
|
||||
`dungeon:wailing-caverns`. The complete publication catalog retains all 54
|
||||
regular dungeons. Internal `manastorm:*` environment packs remain dependency
|
||||
targets for the 53 regular dungeons that reuse those maps, but they and
|
||||
`core:arenas` are hidden from the Downloads screen.
|
||||
|
||||
The staging directories contain one object per original SHA-256 and extension
|
||||
under `content/bundled`. Gzip is used only when it saves space. The packager
|
||||
expands each compressed object and compares every byte with its source before
|
||||
writing the bootstrap catalog. Source GLBs, texture resolutions, animations,
|
||||
and audio are unchanged. No media is generated by this pipeline.
|
||||
|
||||
Compressed objects end in `.gzip`, not `.gz`: Android's asset packager expands
|
||||
and renames `.gz` inputs. The `.gzip` extension is explicitly stored without
|
||||
additional ZIP compression so the manifest and APK preserve identical bytes.
|
||||
|
||||
Each logical asset keeps its original byte count and SHA-256 in the manifest.
|
||||
An optional `download` record describes its gzip representation, including a
|
||||
separate compressed hash and byte count. The schema remains compatible with
|
||||
existing catalogs. Remote publishing retains original objects for older apps;
|
||||
new clients prefer the compressed representation and verify both hashes.
|
||||
|
||||
The Node server and Vite preview middleware resolve bundled logical URLs on
|
||||
first launch. The browser service worker does the same after activation and
|
||||
persists each immutable bootstrap object by hash after its first use.
|
||||
Android streams bundled gzip through its WebView asset handler, including the
|
||||
companion display, without writing an expanded copy to app storage. Deploy the
|
||||
browser build with the project's Node server so first-launch asset aliases work.
|
||||
|
||||
Browser optional installs store compressed objects once and keep small logical
|
||||
aliases. Legacy uncompressed cache entries remain readable. Removing a pack
|
||||
removes an object only after its last installed reference is gone. Android
|
||||
optional downloads resume the compressed transfer, verify it, expand and verify
|
||||
the original, and atomically activate the existing shared native object file.
|
||||
|
||||
`content/storage-report.json` inside each staging/build directory records actual
|
||||
unique object bytes and duplicate savings. These are packaged asset sizes, not
|
||||
GPU memory estimates. Optional Android downloads occupy their expanded size;
|
||||
bundled Android assets stay compressed inside the APK.
|
||||
|
||||
Normal release verification rejects unexpected bootstrap packs, more than 80
|
||||
MiB of stored bootstrap objects, arena assets, and shelved client modules.
|
||||
Eligible JavaScript, CSS, and JSON responses are precompressed with Brotli and
|
||||
gzip; hashed Vite files and bundled content objects use immutable one-year
|
||||
caching, while HTML, service workers, and manifests revalidate.
|
||||
|
||||
Validation: `npm run content:test`, the content/avatar Vitest suites, the
|
||||
bundled-content server tests, `npm run build`, Android compilation, and a live
|
||||
roster plus dungeon screenshot check. Run `npm run release:check` before handoff.
|
||||
Android build commands also run `scripts/content/verify-android-package.py`
|
||||
against the resulting APK, checking every stored object and every original hash.
|
||||
@@ -1,8 +1,17 @@
|
||||
# Five-player dungeon import checklist
|
||||
|
||||
Each dungeon is complete only when its actual instance environment, collision,
|
||||
This checklist tracks the native asset and population import, not complete
|
||||
instance gameplay. A checked dungeon has its environment, collision,
|
||||
navigation, AzerothCore population, boss roster, and native animated creature
|
||||
models are packaged under `public/assets/game/dungeons/<slug>/`.
|
||||
models packaged under `public/assets/game/dungeons/<slug>/`. Use
|
||||
`WOW_DUNGEON_FUNCTIONALITY_REPORT.md` for the separate production-fidelity gate
|
||||
covering doors, objects, scripted progression, waves, escorts, vehicles,
|
||||
traversal, resets, checkpoints, and completion playtests.
|
||||
|
||||
Instance-source review status (2026-09-08): 54/54 reviewed, 51/54 backed by an
|
||||
executable instance controller, and 3/54 pinned as source-exact no-op instance
|
||||
scripts. These counts describe implemented runtime coverage; they do not close
|
||||
the native game-object export or end-to-end playtest gates.
|
||||
|
||||
Run one dungeon at a time:
|
||||
|
||||
@@ -88,24 +97,28 @@ TypeScript/production bundle checks passed.
|
||||
|
||||
## Ascension-specific five-player instances
|
||||
|
||||
- [ ] Tinkertech Showdown (`821`) - blocked on authoritative Ascension server
|
||||
population/model data. Encounter identity exists for Mechatron (`663012`),
|
||||
but no creature template, display mapping, or spawn rows exist locally.
|
||||
- [ ] Frozen Reach (`908`) - blocked on authoritative Ascension server
|
||||
population/model data. Encounter identity exists for Alva (`97700`), but no
|
||||
creature template, display mapping, or spawn rows exist locally.
|
||||
- [ ] Forgotten Mine (`1781`) - blocked on authoritative Ascension server
|
||||
population/model data. Encounter identity exists for Geolord Vek (`254272`),
|
||||
but no creature template, display mapping, or spawn rows exist locally.
|
||||
- [ ] Tinkertech Showdown (`821`) - playable HealerMan-authored adaptation.
|
||||
Mechatron (`663012`) and the environment are exact; the candidate machine
|
||||
spell cluster, trash, cadence, positions, models, and phase rules remain
|
||||
explicitly authored because no server binding or spawn snapshot survived.
|
||||
- [ ] Frozen Reach (`908`) - playable source-backed adaptation. Alva (`97700`),
|
||||
Arktos (`97701`), 85 exact spell rows, and summon links for entries `97703`,
|
||||
`97731`, and `97770` were recovered. Server cadence, positions, stats,
|
||||
models, targeting, and exact phase behavior remain authored.
|
||||
- [ ] Forgotten Mine (`1781`) - playable source-backed adaptation. Geolord Vek
|
||||
(`254272`), 33 exact spell rows, Geospike (`254278`), and Resonite Shard
|
||||
(`254051`) were recovered. Server cadence, positions, trash, models, and the
|
||||
exact crystal rule remain authored.
|
||||
|
||||
The individual command has been run for all three blocked instances. Their maps
|
||||
and encounter identities are present in the installed client, but the installed
|
||||
client and available creature caches do not provide the server-only
|
||||
entry-to-display and spawn data needed to package their actual mobs/bosses.
|
||||
They intentionally remain on the documented procedural fallback until an
|
||||
Ascension server snapshot or reviewed creature/spawn export is supplied.
|
||||
The individual import command has been run for all three source-limited
|
||||
instances. Their checksummed recovery manifest lives under `ascension/` and is
|
||||
validated by `npm run dungeon:ascension:recovery:validate`. Runtime adapters
|
||||
consume the recovered evidence while retaining explicit `source-backed-adapted`
|
||||
or `healer-man-authored` labels. The checklist remains open until an
|
||||
authoritative Ascension creature/spawn/object/server-script snapshot and native
|
||||
display mappings replace the remaining authored portions.
|
||||
|
||||
## Per-dungeon completion gate
|
||||
## Per-dungeon asset/import gate
|
||||
|
||||
These gates passed for every checked dungeon above:
|
||||
|
||||
@@ -116,3 +129,7 @@ These gates passed for every checked dungeon above:
|
||||
- [x] Boss order, entrance, and coordinates reviewed.
|
||||
- [x] Generated campaign catalog points at `/assets/game/dungeons/<slug>/`.
|
||||
- [x] Catalog verification, tests, type-check, and production bundle pass.
|
||||
|
||||
These checks do not certify instance-script behavior. A dungeon remains
|
||||
functionally incomplete until its entry in `WOW_DUNGEON_FUNCTIONALITY_REPORT.md`
|
||||
is green.
|
||||
+71
-15
@@ -73,10 +73,29 @@ attaches native animated client models by display ID. It writes:
|
||||
- `src/game/generated/dungeonCampaignCatalog.compressed.ts`, the bundled catalog
|
||||
- `src/game/generated/dungeonAvailability.json`, the generated availability list
|
||||
|
||||
Boss specials are compiled as independent mechanic records. AzerothCore
|
||||
SmartAI timers and health events are imported automatically; C++ encounter
|
||||
spells are imported from the pinned script checkout. Add or override a boss in
|
||||
`dungeon-pipeline/boss-mechanics.json` when an encounter needs hand-authored
|
||||
targeting, summon behavior, timing, or telegraph geometry. Use `mode: "replace"`
|
||||
to make that list authoritative or `mode: "append"` to layer entries ahead of
|
||||
the imported mechanics, then run `npm run dungeon:catalog`. The generated
|
||||
`combat.mechanicIds` list is the stable loadout boundary intended for later
|
||||
encounter randomization.
|
||||
|
||||
The same catalog also stores Runewaker kits under
|
||||
`rw-<dungeon>-<entity-id>` keys. Run `npm run runewaker:bosses:refresh` to
|
||||
rebuild those records from preserved RuneWaker spell/Lua evidence, regenerate
|
||||
the population packages, and run `npm run runewaker:audit:gameplay`. A record
|
||||
whose preserved source has no recoverable combat ability is explicitly marked
|
||||
`source.kind: "healer-man-source-gap"` rather than being claimed as authentic.
|
||||
|
||||
Stock AzerothCore population is used for the standard dungeons. The three
|
||||
Ascension-specific instances—Tinkertech Showdown, Frozen Reach, and Forgotten
|
||||
Mine—keep their client encounter identities and use an explicit procedural
|
||||
trash-population fallback. Non-Epoch recipes keep explicit procedural actor
|
||||
Mine—keep their recovered client identities, spell/summon evidence where
|
||||
available, and explicit source-backed or HealerMan-authored runtime adaptations.
|
||||
Their remaining procedural population is never labeled authoritative.
|
||||
Non-Epoch recipes keep explicit procedural actor
|
||||
bodies until reviewed display exports are available. Epoch actors use packaged
|
||||
client GLBs with their complete native animation libraries; only event-only
|
||||
controllers without a creature display fall back.
|
||||
@@ -143,10 +162,13 @@ The audit verifies the complete 54-dungeon catalog, authoritative/fallback
|
||||
classification, every native environment and creature URL, file sizes,
|
||||
checksums, animation presence, and compiled entry-to-model bindings. The
|
||||
2026-07-26 batch packaged 50 native imports; together with the Wailing Caverns
|
||||
golden fixture, 51/54 dungeons are authoritative. Tinkertech Showdown, Frozen
|
||||
Reach, and Forgotten Mine remain documented fallbacks because available local
|
||||
sources contain their maps and encounter identities but no authoritative
|
||||
Ascension creature templates, display mappings, or spawn rows.
|
||||
golden fixture, 51/54 dungeons have authoritative WoW population packages.
|
||||
Tinkertech Showdown, Frozen Reach, and Forgotten Mine now have playable,
|
||||
provenance-labeled runtime adaptations backed by
|
||||
`ascension/ASCENSION_CUSTOM_DUNGEON_RECOVERY.json`. They remain outside the
|
||||
authoritative package count because the recovered client evidence does not
|
||||
include complete Ascension creature templates, display mappings, spawn/object
|
||||
coordinates, or server-side cadence.
|
||||
|
||||
1. `npm run dungeon:discover -- <slug>` writes deterministic client/server
|
||||
snapshots, a provenance lock, review-required overrides, and—when server
|
||||
@@ -207,9 +229,14 @@ The default pass recompiles the 54-dungeon campaign, verifies all packaged
|
||||
environment and creature assets, audits every mob and boss attack/mechanic,
|
||||
opens every unique native creature GLB to verify idle, movement, attack, wound,
|
||||
and death clips, proves the procedural animation path for model-less actors,
|
||||
and runs the actual runtime-registry coverage test. It writes deterministic
|
||||
JSON and Markdown reports to WOW_DUNGEON_CONTENT_REPORT.json and
|
||||
WOW_DUNGEON_CONTENT_REPORT.md.
|
||||
runs the actual runtime-registry coverage test, and audits instance-level
|
||||
functionality. It writes deterministic JSON and Markdown reports to
|
||||
WOW_DUNGEON_CONTENT_REPORT.json, WOW_DUNGEON_CONTENT_REPORT.md,
|
||||
WOW_DUNGEON_FUNCTIONALITY_REPORT.json, and
|
||||
WOW_DUNGEON_FUNCTIONALITY_REPORT.md. Catalog compilation also writes
|
||||
WOW_GAMEOBJECT_ASSET_MANIFEST.json, which deduplicates the source-backed object
|
||||
spawns by client model path, preserves per-dungeon/display usage, assigns the
|
||||
planned shipping GLB URL, and lists unresolved display models and bounds.
|
||||
|
||||
Useful batch options:
|
||||
|
||||
@@ -222,11 +249,40 @@ Useful batch options:
|
||||
# Re-run green packages, then build the app after validation.
|
||||
npm run dungeon:wow:all -- --refresh --force --build
|
||||
|
||||
# Only re-run combat, mechanic, and animation coverage against current output.
|
||||
# Re-run combat, animation, and instance-functionality audits against current output.
|
||||
npm run dungeon:wow:all -- --audit-only
|
||||
|
||||
Kresh is intentionally recorded as a source-faithful melee-only boss: entry
|
||||
3653 has no spell or SmartAI row in the pinned AzerothCore snapshot. Trial of
|
||||
the Champion is likewise documented as a boss-arena population rather than a
|
||||
trash route. These exceptions are asserted by exact dungeon/entity IDs; any
|
||||
new basic-only boss or mob-less dungeon fails the batch.
|
||||
The functionality report is the production-fidelity gate. It separately checks
|
||||
typed world-object contracts, source instance scripts, encounter orchestration,
|
||||
required interactions, traversal evidence, resets, checkpoints, and completion
|
||||
playtests. A green asset or combat audit does not imply that doors, escorts,
|
||||
waves, vehicles, keys, chests, or scripted instance progression are implemented.
|
||||
The report fails closed and lists concrete edit targets for every remaining
|
||||
dungeon gap.
|
||||
|
||||
The 2026-09-08 implementation sweep completed an instance-level source review
|
||||
for all 54 campaign dungeons. Fifty-one now have runtime-wired reviewed
|
||||
orchestration in addition to fleet boss/checkpoint/completion declarations; the
|
||||
three stock instance scripts that are genuinely empty are pinned and recorded
|
||||
as source-exact no-ops. This means there is no remaining unreviewed instance
|
||||
script, not that every source feature is already exact or live-traversal validated. Per-dungeon adaptations,
|
||||
missing primitives, native-object export work, and live traversal evidence stay
|
||||
open in `WOW_DUNGEON_FUNCTIONALITY_REPORT.md`.
|
||||
|
||||
Source-backed object records stay hidden, non-blocking, and non-interactive
|
||||
until their manifest asset has been exported and the corresponding instance
|
||||
script transition has been reviewed. This lets the typed data ship through the
|
||||
runtime contract without leaking placeholder geometry or turning an incomplete
|
||||
door/transport port into a dungeon soft lock.
|
||||
|
||||
Kresh, the Deviate Faerie Dragon, Grubbis, Spirestone Battle Lord, Spirestone
|
||||
Butcher, and Crystal Fang are intentionally recorded as source-faithful
|
||||
melee-only bosses. Kresh entry 3653 and the Deviate Faerie Dragon entry 5912
|
||||
have no spell or SmartAI row in the pinned AzerothCore snapshot; the latter is
|
||||
also despawned by the Wailing Caverns initialization event. Grubbis entry 7361
|
||||
has only aggro dialogue and death-completion rows, with no combat spell cast.
|
||||
The three Blackrock Spire entries are 15% rare spawns whose database rows
|
||||
contain no active combat cast; Crystal Fang's exact on-death Spire Spiderling
|
||||
summon is implemented by the reviewed instance adapter. One shared exception
|
||||
table supplies the runtime test and composed-content audit; stale, duplicate,
|
||||
or newly introduced basic-only boss keys fail the batch.
|
||||
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"status": "green",
|
||||
"scope": "HealerMan WoW five-player campaign",
|
||||
"scope": "HealerMan WoW five-player combat and animation coverage",
|
||||
"summary": {
|
||||
"dungeons": 54,
|
||||
"mobs": 1031,
|
||||
"bosses": 253,
|
||||
"attacks": 2953,
|
||||
"specialAttacks": 1669,
|
||||
"nativeModels": 1266,
|
||||
"proceduralModels": 18,
|
||||
"uniqueNativeModels": 1166,
|
||||
"nativeStateFallbacks": 16
|
||||
"mobs": 1193,
|
||||
"bosses": 273,
|
||||
"attacks": 4026,
|
||||
"specialAttacks": 2572,
|
||||
"nativeModels": 1275,
|
||||
"proceduralModels": 191,
|
||||
"uniqueNativeModels": 1167,
|
||||
"nativeStateFallbacks": 18
|
||||
},
|
||||
"documentedExceptions": [
|
||||
{
|
||||
"type": "basic-only-boss",
|
||||
"key": "wailing-caverns:entry-3653",
|
||||
"key": "wailing-caverns:kresh",
|
||||
"reason": "Kresh is melee-only in the pinned AzerothCore snapshot; the historical server has no spell or SmartAI row for entry 3653."
|
||||
},
|
||||
{
|
||||
@@ -25,9 +25,24 @@
|
||||
"reason": "The server-faithful rare entry 5912 has no spell rows and is despawned by the Wailing Caverns initialization event."
|
||||
},
|
||||
{
|
||||
"type": "no-trash-instance",
|
||||
"key": "trial-of-the-champion",
|
||||
"reason": "Trial of the Champion is an encounter arena; its imported combat population is the five Grand Champion bosses, not a trash route."
|
||||
"type": "basic-only-boss",
|
||||
"key": "gnomeregan:wow-classic-batch-three-entry-7361",
|
||||
"reason": "Grubbis is melee-only in the pinned AzerothCore snapshot; entry 7361 has aggro dialogue and instance-completion rows but no combat spell cast."
|
||||
},
|
||||
{
|
||||
"type": "basic-only-boss",
|
||||
"key": "blackrock-spire:entry-9218",
|
||||
"reason": "Spirestone Battle Lord is a 15% rare spawn in the pinned database and has no combat spell cast beyond its basic melee attack."
|
||||
},
|
||||
{
|
||||
"type": "basic-only-boss",
|
||||
"key": "blackrock-spire:entry-9219",
|
||||
"reason": "Spirestone Butcher is a 15% rare spawn in the pinned database and has no combat spell cast beyond its basic melee attack."
|
||||
},
|
||||
{
|
||||
"type": "basic-only-boss",
|
||||
"key": "blackrock-spire:entry-10376",
|
||||
"reason": "Crystal Fang is a 15% rare spawn with no active combat spell in the pinned database; its exact on-death Spire Spiderling summon is implemented by the instance adapter."
|
||||
}
|
||||
],
|
||||
"animationFallbacks": [
|
||||
@@ -47,6 +62,14 @@
|
||||
],
|
||||
"renderer": "OriginalCreatureModel combat fallback"
|
||||
},
|
||||
{
|
||||
"url": "/assets/game/dungeons/dire-maul/creatures/display-14430-animated.glb",
|
||||
"state": "move",
|
||||
"users": [
|
||||
"dire-maul:entry-14386"
|
||||
],
|
||||
"renderer": "AnimatedMobBody locomotion fallback"
|
||||
},
|
||||
{
|
||||
"url": "/assets/game/dungeons/dire-maul/creatures/display-14430-animated.glb",
|
||||
"state": "attack",
|
||||
@@ -56,7 +79,15 @@
|
||||
"renderer": "OriginalCreatureModel combat fallback"
|
||||
},
|
||||
{
|
||||
"url": "/assets/game/creatures/shared/2fbb8d2226a7fdf3e9a7af13568f6bb46cb1b27afc008732832b9fd42c2e510e.glb",
|
||||
"url": "/assets/game/dungeons/dire-maul/creatures/display-14430-animated.glb",
|
||||
"state": "wound",
|
||||
"users": [
|
||||
"dire-maul:entry-14386"
|
||||
],
|
||||
"renderer": "OriginalCreatureModel combat fallback"
|
||||
},
|
||||
{
|
||||
"url": "/assets/game/creatures/shared/52c8b3aea63f21f8e98ad84eaf601f2918d1df17a05525d41f5b3e17da53c8ff.glb",
|
||||
"state": "wound",
|
||||
"users": [
|
||||
"steamvault:entry-21694",
|
||||
@@ -164,14 +195,26 @@
|
||||
],
|
||||
"issues": [],
|
||||
"dungeons": [
|
||||
{
|
||||
"id": "wailing-caverns",
|
||||
"title": "Wailing Caverns",
|
||||
"source": "azerothcore",
|
||||
"mobs": 23,
|
||||
"bosses": 9,
|
||||
"attacks": 72,
|
||||
"specialAttacks": 40,
|
||||
"nativeModels": 24,
|
||||
"proceduralModels": 8,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "shadowfang-keep",
|
||||
"title": "Shadowfang Keep",
|
||||
"source": "azerothcore",
|
||||
"mobs": 24,
|
||||
"bosses": 7,
|
||||
"attacks": 58,
|
||||
"specialAttacks": 27,
|
||||
"attacks": 65,
|
||||
"specialAttacks": 34,
|
||||
"nativeModels": 31,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -182,8 +225,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 6,
|
||||
"bosses": 5,
|
||||
"attacks": 32,
|
||||
"specialAttacks": 21,
|
||||
"attacks": 42,
|
||||
"specialAttacks": 31,
|
||||
"nativeModels": 11,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -194,32 +237,20 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 15,
|
||||
"bosses": 7,
|
||||
"attacks": 48,
|
||||
"specialAttacks": 26,
|
||||
"attacks": 55,
|
||||
"specialAttacks": 33,
|
||||
"nativeModels": 22,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "wailing-caverns",
|
||||
"title": "Wailing Caverns",
|
||||
"source": "azerothcore",
|
||||
"mobs": 12,
|
||||
"bosses": 8,
|
||||
"attacks": 51,
|
||||
"specialAttacks": 31,
|
||||
"nativeModels": 15,
|
||||
"proceduralModels": 5,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "razorfen-kraul",
|
||||
"title": "Razorfen Kraul",
|
||||
"source": "azerothcore",
|
||||
"mobs": 31,
|
||||
"bosses": 7,
|
||||
"attacks": 78,
|
||||
"specialAttacks": 40,
|
||||
"attacks": 87,
|
||||
"specialAttacks": 49,
|
||||
"nativeModels": 38,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -228,12 +259,12 @@
|
||||
"id": "blackfathom-deeps",
|
||||
"title": "Blackfathom Deeps",
|
||||
"source": "azerothcore",
|
||||
"mobs": 21,
|
||||
"mobs": 23,
|
||||
"bosses": 6,
|
||||
"attacks": 58,
|
||||
"specialAttacks": 31,
|
||||
"attacks": 67,
|
||||
"specialAttacks": 38,
|
||||
"nativeModels": 27,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 2,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
@@ -242,8 +273,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 31,
|
||||
"bosses": 7,
|
||||
"attacks": 85,
|
||||
"specialAttacks": 47,
|
||||
"attacks": 104,
|
||||
"specialAttacks": 66,
|
||||
"nativeModels": 38,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -252,36 +283,36 @@
|
||||
"id": "gnomeregan",
|
||||
"title": "Gnomeregan",
|
||||
"source": "azerothcore",
|
||||
"mobs": 27,
|
||||
"bosses": 4,
|
||||
"attacks": 68,
|
||||
"specialAttacks": 37,
|
||||
"mobs": 28,
|
||||
"bosses": 5,
|
||||
"attacks": 84,
|
||||
"specialAttacks": 51,
|
||||
"nativeModels": 31,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 2,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "sunken-temple",
|
||||
"title": "Sunken Temple",
|
||||
"source": "azerothcore",
|
||||
"mobs": 29,
|
||||
"mobs": 31,
|
||||
"bosses": 4,
|
||||
"attacks": 86,
|
||||
"specialAttacks": 53,
|
||||
"attacks": 95,
|
||||
"specialAttacks": 60,
|
||||
"nativeModels": 33,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 2,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "razorfen-downs",
|
||||
"title": "Razorfen Downs",
|
||||
"source": "azerothcore",
|
||||
"mobs": 19,
|
||||
"mobs": 21,
|
||||
"bosses": 6,
|
||||
"attacks": 55,
|
||||
"specialAttacks": 30,
|
||||
"attacks": 65,
|
||||
"specialAttacks": 38,
|
||||
"nativeModels": 25,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 2,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
@@ -290,8 +321,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 32,
|
||||
"bosses": 5,
|
||||
"attacks": 89,
|
||||
"specialAttacks": 52,
|
||||
"attacks": 107,
|
||||
"specialAttacks": 70,
|
||||
"nativeModels": 37,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -300,60 +331,60 @@
|
||||
"id": "zulfarrak",
|
||||
"title": "Zul'Farrak",
|
||||
"source": "azerothcore",
|
||||
"mobs": 16,
|
||||
"mobs": 23,
|
||||
"bosses": 5,
|
||||
"attacks": 53,
|
||||
"specialAttacks": 32,
|
||||
"attacks": 76,
|
||||
"specialAttacks": 48,
|
||||
"nativeModels": 21,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 7,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "blackrock-spire",
|
||||
"title": "Blackrock Spire",
|
||||
"source": "azerothcore",
|
||||
"mobs": 63,
|
||||
"bosses": 15,
|
||||
"attacks": 210,
|
||||
"specialAttacks": 132,
|
||||
"mobs": 66,
|
||||
"bosses": 17,
|
||||
"attacks": 285,
|
||||
"specialAttacks": 202,
|
||||
"nativeModels": 78,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 5,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "blackrock-depths",
|
||||
"title": "Blackrock Depths",
|
||||
"source": "azerothcore",
|
||||
"mobs": 48,
|
||||
"mobs": 58,
|
||||
"bosses": 13,
|
||||
"attacks": 145,
|
||||
"specialAttacks": 84,
|
||||
"attacks": 192,
|
||||
"specialAttacks": 121,
|
||||
"nativeModels": 61,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 10,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "scholomance",
|
||||
"title": "Scholomance",
|
||||
"source": "azerothcore",
|
||||
"mobs": 30,
|
||||
"mobs": 31,
|
||||
"bosses": 8,
|
||||
"attacks": 115,
|
||||
"specialAttacks": 77,
|
||||
"attacks": 151,
|
||||
"specialAttacks": 112,
|
||||
"nativeModels": 38,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 1,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "stratholme",
|
||||
"title": "Stratholme",
|
||||
"source": "azerothcore",
|
||||
"mobs": 41,
|
||||
"mobs": 45,
|
||||
"bosses": 9,
|
||||
"attacks": 131,
|
||||
"specialAttacks": 81,
|
||||
"attacks": 180,
|
||||
"specialAttacks": 126,
|
||||
"nativeModels": 50,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 4,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
@@ -362,8 +393,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 25,
|
||||
"bosses": 8,
|
||||
"attacks": 83,
|
||||
"specialAttacks": 50,
|
||||
"attacks": 95,
|
||||
"specialAttacks": 62,
|
||||
"nativeModels": 33,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -374,8 +405,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 7,
|
||||
"bosses": 4,
|
||||
"attacks": 21,
|
||||
"specialAttacks": 10,
|
||||
"attacks": 23,
|
||||
"specialAttacks": 12,
|
||||
"nativeModels": 11,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -386,8 +417,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 63,
|
||||
"bosses": 12,
|
||||
"attacks": 184,
|
||||
"specialAttacks": 109,
|
||||
"attacks": 242,
|
||||
"specialAttacks": 167,
|
||||
"nativeModels": 75,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -398,8 +429,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 29,
|
||||
"bosses": 3,
|
||||
"attacks": 74,
|
||||
"specialAttacks": 42,
|
||||
"attacks": 92,
|
||||
"specialAttacks": 60,
|
||||
"nativeModels": 32,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -410,8 +441,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 14,
|
||||
"bosses": 3,
|
||||
"attacks": 44,
|
||||
"specialAttacks": 27,
|
||||
"attacks": 53,
|
||||
"specialAttacks": 36,
|
||||
"nativeModels": 17,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -420,12 +451,12 @@
|
||||
"id": "hellfire-ramparts",
|
||||
"title": "Hellfire Citadel: Ramparts",
|
||||
"source": "azerothcore",
|
||||
"mobs": 10,
|
||||
"bosses": 2,
|
||||
"attacks": 36,
|
||||
"specialAttacks": 24,
|
||||
"mobs": 11,
|
||||
"bosses": 4,
|
||||
"attacks": 47,
|
||||
"specialAttacks": 32,
|
||||
"nativeModels": 12,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 3,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
@@ -434,8 +465,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 14,
|
||||
"bosses": 3,
|
||||
"attacks": 50,
|
||||
"specialAttacks": 33,
|
||||
"attacks": 63,
|
||||
"specialAttacks": 46,
|
||||
"nativeModels": 17,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -444,48 +475,48 @@
|
||||
"id": "underbog",
|
||||
"title": "Coilfang: The Underbog",
|
||||
"source": "azerothcore",
|
||||
"mobs": 18,
|
||||
"mobs": 20,
|
||||
"bosses": 4,
|
||||
"attacks": 59,
|
||||
"specialAttacks": 37,
|
||||
"attacks": 82,
|
||||
"specialAttacks": 58,
|
||||
"nativeModels": 22,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 2,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "slave-pens",
|
||||
"title": "Coilfang: The Slave Pens",
|
||||
"source": "azerothcore",
|
||||
"mobs": 18,
|
||||
"bosses": 3,
|
||||
"attacks": 53,
|
||||
"specialAttacks": 32,
|
||||
"mobs": 21,
|
||||
"bosses": 4,
|
||||
"attacks": 66,
|
||||
"specialAttacks": 43,
|
||||
"nativeModels": 21,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 4,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "arcatraz",
|
||||
"title": "Tempest Keep: The Arcatraz",
|
||||
"source": "azerothcore",
|
||||
"mobs": 25,
|
||||
"mobs": 28,
|
||||
"bosses": 4,
|
||||
"attacks": 73,
|
||||
"specialAttacks": 44,
|
||||
"attacks": 108,
|
||||
"specialAttacks": 76,
|
||||
"nativeModels": 29,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 3,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "botanica",
|
||||
"title": "Tempest Keep: The Botanica",
|
||||
"source": "azerothcore",
|
||||
"mobs": 24,
|
||||
"mobs": 28,
|
||||
"bosses": 5,
|
||||
"attacks": 67,
|
||||
"specialAttacks": 38,
|
||||
"attacks": 89,
|
||||
"specialAttacks": 56,
|
||||
"nativeModels": 29,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 4,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
@@ -494,8 +525,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 14,
|
||||
"bosses": 3,
|
||||
"attacks": 45,
|
||||
"specialAttacks": 28,
|
||||
"attacks": 69,
|
||||
"specialAttacks": 52,
|
||||
"nativeModels": 17,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -506,8 +537,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 18,
|
||||
"bosses": 4,
|
||||
"attacks": 46,
|
||||
"specialAttacks": 24,
|
||||
"attacks": 67,
|
||||
"specialAttacks": 45,
|
||||
"nativeModels": 22,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -516,72 +547,72 @@
|
||||
"id": "sethekk-halls",
|
||||
"title": "Auchindoun: Sethekk Halls",
|
||||
"source": "azerothcore",
|
||||
"mobs": 14,
|
||||
"bosses": 2,
|
||||
"attacks": 39,
|
||||
"specialAttacks": 23,
|
||||
"mobs": 19,
|
||||
"bosses": 3,
|
||||
"attacks": 59,
|
||||
"specialAttacks": 37,
|
||||
"nativeModels": 16,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 6,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "mana-tombs",
|
||||
"title": "Auchindoun: Mana-Tombs",
|
||||
"source": "azerothcore",
|
||||
"mobs": 11,
|
||||
"bosses": 3,
|
||||
"attacks": 33,
|
||||
"specialAttacks": 19,
|
||||
"mobs": 12,
|
||||
"bosses": 4,
|
||||
"attacks": 50,
|
||||
"specialAttacks": 34,
|
||||
"nativeModels": 14,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 2,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "auchenai-crypts",
|
||||
"title": "Auchindoun: Auchenai Crypts",
|
||||
"source": "azerothcore",
|
||||
"mobs": 12,
|
||||
"bosses": 2,
|
||||
"attacks": 30,
|
||||
"specialAttacks": 16,
|
||||
"mobs": 13,
|
||||
"bosses": 3,
|
||||
"attacks": 48,
|
||||
"specialAttacks": 32,
|
||||
"nativeModels": 14,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 2,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "magisters-terrace",
|
||||
"title": "Magister's Terrace",
|
||||
"source": "azerothcore",
|
||||
"mobs": 15,
|
||||
"mobs": 23,
|
||||
"bosses": 3,
|
||||
"attacks": 33,
|
||||
"specialAttacks": 15,
|
||||
"attacks": 65,
|
||||
"specialAttacks": 39,
|
||||
"nativeModels": 18,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 8,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "black-morass",
|
||||
"title": "The Black Morass",
|
||||
"source": "azerothcore",
|
||||
"mobs": 4,
|
||||
"mobs": 13,
|
||||
"bosses": 3,
|
||||
"attacks": 20,
|
||||
"specialAttacks": 13,
|
||||
"attacks": 34,
|
||||
"specialAttacks": 18,
|
||||
"nativeModels": 7,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 9,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "escape-from-durnholde",
|
||||
"title": "The Escape From Durnholde",
|
||||
"source": "azerothcore",
|
||||
"mobs": 26,
|
||||
"mobs": 32,
|
||||
"bosses": 3,
|
||||
"attacks": 45,
|
||||
"specialAttacks": 16,
|
||||
"attacks": 59,
|
||||
"specialAttacks": 24,
|
||||
"nativeModels": 29,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 6,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
@@ -589,107 +620,107 @@
|
||||
"title": "Utgarde Keep",
|
||||
"source": "azerothcore",
|
||||
"mobs": 19,
|
||||
"bosses": 3,
|
||||
"attacks": 35,
|
||||
"specialAttacks": 13,
|
||||
"bosses": 6,
|
||||
"attacks": 55,
|
||||
"specialAttacks": 30,
|
||||
"nativeModels": 22,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 3,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "utgarde-pinnacle",
|
||||
"title": "Utgarde Pinnacle",
|
||||
"source": "azerothcore",
|
||||
"mobs": 13,
|
||||
"mobs": 19,
|
||||
"bosses": 4,
|
||||
"attacks": 32,
|
||||
"specialAttacks": 15,
|
||||
"attacks": 50,
|
||||
"specialAttacks": 27,
|
||||
"nativeModels": 17,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 6,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "the-nexus",
|
||||
"title": "The Nexus",
|
||||
"source": "azerothcore",
|
||||
"mobs": 21,
|
||||
"mobs": 24,
|
||||
"bosses": 5,
|
||||
"attacks": 50,
|
||||
"specialAttacks": 24,
|
||||
"attacks": 64,
|
||||
"specialAttacks": 35,
|
||||
"nativeModels": 26,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 3,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "the-oculus",
|
||||
"title": "The Oculus",
|
||||
"source": "azerothcore",
|
||||
"mobs": 8,
|
||||
"mobs": 17,
|
||||
"bosses": 4,
|
||||
"attacks": 27,
|
||||
"specialAttacks": 15,
|
||||
"attacks": 42,
|
||||
"specialAttacks": 21,
|
||||
"nativeModels": 12,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 9,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "culling-of-stratholme",
|
||||
"title": "The Culling of Stratholme",
|
||||
"source": "azerothcore",
|
||||
"mobs": 16,
|
||||
"mobs": 21,
|
||||
"bosses": 4,
|
||||
"attacks": 41,
|
||||
"specialAttacks": 21,
|
||||
"attacks": 49,
|
||||
"specialAttacks": 24,
|
||||
"nativeModels": 20,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 5,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "halls-of-stone",
|
||||
"title": "Halls of Stone",
|
||||
"source": "azerothcore",
|
||||
"mobs": 13,
|
||||
"mobs": 16,
|
||||
"bosses": 4,
|
||||
"attacks": 32,
|
||||
"specialAttacks": 15,
|
||||
"attacks": 45,
|
||||
"specialAttacks": 25,
|
||||
"nativeModels": 16,
|
||||
"proceduralModels": 1,
|
||||
"proceduralModels": 4,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "draktharon-keep",
|
||||
"title": "Drak'Tharon Keep",
|
||||
"source": "azerothcore",
|
||||
"mobs": 18,
|
||||
"mobs": 20,
|
||||
"bosses": 4,
|
||||
"attacks": 38,
|
||||
"specialAttacks": 16,
|
||||
"attacks": 47,
|
||||
"specialAttacks": 23,
|
||||
"nativeModels": 22,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 2,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "azjol-nerub",
|
||||
"title": "Azjol-Nerub",
|
||||
"source": "azerothcore",
|
||||
"mobs": 10,
|
||||
"mobs": 18,
|
||||
"bosses": 3,
|
||||
"attacks": 25,
|
||||
"specialAttacks": 12,
|
||||
"attacks": 38,
|
||||
"specialAttacks": 17,
|
||||
"nativeModels": 13,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 8,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "halls-of-lightning",
|
||||
"title": "Halls of Lightning",
|
||||
"source": "azerothcore",
|
||||
"mobs": 16,
|
||||
"mobs": 19,
|
||||
"bosses": 4,
|
||||
"attacks": 35,
|
||||
"specialAttacks": 15,
|
||||
"attacks": 52,
|
||||
"specialAttacks": 29,
|
||||
"nativeModels": 20,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 3,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
@@ -698,8 +729,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 15,
|
||||
"bosses": 5,
|
||||
"attacks": 43,
|
||||
"specialAttacks": 23,
|
||||
"attacks": 53,
|
||||
"specialAttacks": 33,
|
||||
"nativeModels": 20,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -708,24 +739,24 @@
|
||||
"id": "violet-hold",
|
||||
"title": "Violet Hold",
|
||||
"source": "azerothcore",
|
||||
"mobs": 2,
|
||||
"mobs": 17,
|
||||
"bosses": 3,
|
||||
"attacks": 19,
|
||||
"specialAttacks": 14,
|
||||
"attacks": 39,
|
||||
"specialAttacks": 19,
|
||||
"nativeModels": 5,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 15,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "ahnkahet-old-kingdom",
|
||||
"title": "Ahn'kahet: The Old Kingdom",
|
||||
"source": "azerothcore",
|
||||
"mobs": 19,
|
||||
"mobs": 23,
|
||||
"bosses": 5,
|
||||
"attacks": 43,
|
||||
"specialAttacks": 19,
|
||||
"attacks": 64,
|
||||
"specialAttacks": 36,
|
||||
"nativeModels": 24,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 4,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
@@ -734,8 +765,8 @@
|
||||
"source": "azerothcore",
|
||||
"mobs": 11,
|
||||
"bosses": 2,
|
||||
"attacks": 21,
|
||||
"specialAttacks": 8,
|
||||
"attacks": 23,
|
||||
"specialAttacks": 10,
|
||||
"nativeModels": 13,
|
||||
"proceduralModels": 0,
|
||||
"status": "green"
|
||||
@@ -744,36 +775,36 @@
|
||||
"id": "trial-of-the-champion",
|
||||
"title": "Trial of the Champion",
|
||||
"source": "azerothcore",
|
||||
"mobs": 0,
|
||||
"bosses": 5,
|
||||
"attacks": 22,
|
||||
"specialAttacks": 17,
|
||||
"mobs": 6,
|
||||
"bosses": 12,
|
||||
"attacks": 123,
|
||||
"specialAttacks": 105,
|
||||
"nativeModels": 5,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 13,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "pit-of-saron",
|
||||
"title": "Pit of Saron",
|
||||
"source": "azerothcore",
|
||||
"mobs": 27,
|
||||
"mobs": 31,
|
||||
"bosses": 3,
|
||||
"attacks": 43,
|
||||
"specialAttacks": 13,
|
||||
"attacks": 53,
|
||||
"specialAttacks": 19,
|
||||
"nativeModels": 30,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 4,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "halls-of-reflection",
|
||||
"title": "Halls of Reflection",
|
||||
"source": "azerothcore",
|
||||
"mobs": 8,
|
||||
"mobs": 16,
|
||||
"bosses": 2,
|
||||
"attacks": 26,
|
||||
"specialAttacks": 16,
|
||||
"attacks": 46,
|
||||
"specialAttacks": 28,
|
||||
"nativeModels": 10,
|
||||
"proceduralModels": 0,
|
||||
"proceduralModels": 8,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
@@ -782,8 +813,8 @@
|
||||
"source": "procedural-fallback",
|
||||
"mobs": 3,
|
||||
"bosses": 1,
|
||||
"attacks": 8,
|
||||
"specialAttacks": 4,
|
||||
"attacks": 13,
|
||||
"specialAttacks": 12,
|
||||
"nativeModels": 0,
|
||||
"proceduralModels": 4,
|
||||
"status": "green"
|
||||
@@ -792,24 +823,24 @@
|
||||
"id": "frozen-reach",
|
||||
"title": "Frozen Reach",
|
||||
"source": "procedural-fallback",
|
||||
"mobs": 3,
|
||||
"mobs": 2,
|
||||
"bosses": 1,
|
||||
"attacks": 8,
|
||||
"specialAttacks": 4,
|
||||
"attacks": 19,
|
||||
"specialAttacks": 19,
|
||||
"nativeModels": 0,
|
||||
"proceduralModels": 4,
|
||||
"proceduralModels": 3,
|
||||
"status": "green"
|
||||
},
|
||||
{
|
||||
"id": "forgotten-mine",
|
||||
"title": "Forgotten Mine",
|
||||
"source": "procedural-fallback",
|
||||
"mobs": 3,
|
||||
"mobs": 4,
|
||||
"bosses": 1,
|
||||
"attacks": 8,
|
||||
"specialAttacks": 4,
|
||||
"attacks": 13,
|
||||
"specialAttacks": 12,
|
||||
"nativeModels": 0,
|
||||
"proceduralModels": 4,
|
||||
"proceduralModels": 5,
|
||||
"status": "green"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,73 +1,78 @@
|
||||
# WoW dungeon content report
|
||||
# WoW dungeon combat and animation report
|
||||
|
||||
Status: **green**
|
||||
|
||||
Coverage: 54 dungeons, 1031 mobs, 253 bosses, 2953 executable attacks, and 1669 special attacks/mechanics.
|
||||
Scope: executable combat populations and creature animation bindings. This report does not certify instance scripts, doors, interactables, escorts, waves, vehicles, or navigation.
|
||||
|
||||
Animation paths: 1266 native model bindings and 18 procedural bindings across 1166 unique native GLBs; 16 missing native states use the shared procedural fallback.
|
||||
Coverage: 54 dungeons, 1193 mobs, 273 bosses, 4026 executable attacks, and 2572 special attacks/mechanics.
|
||||
|
||||
Animation paths: 1275 native model bindings and 191 procedural bindings across 1167 unique native GLBs; 18 missing native states use the shared procedural fallback.
|
||||
|
||||
| Dungeon | Mobs | Bosses | Attacks | Specials | Native | Procedural | Status |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |
|
||||
| Shadowfang Keep | 24 | 7 | 58 | 27 | 31 | 0 | green |
|
||||
| Stormwind Stockade | 6 | 5 | 32 | 21 | 11 | 0 | green |
|
||||
| The Deadmines | 15 | 7 | 48 | 26 | 22 | 0 | green |
|
||||
| Wailing Caverns | 12 | 8 | 51 | 31 | 15 | 5 | green |
|
||||
| Razorfen Kraul | 31 | 7 | 78 | 40 | 38 | 0 | green |
|
||||
| Blackfathom Deeps | 21 | 6 | 58 | 31 | 27 | 0 | green |
|
||||
| Uldaman | 31 | 7 | 85 | 47 | 38 | 0 | green |
|
||||
| Gnomeregan | 27 | 4 | 68 | 37 | 31 | 0 | green |
|
||||
| Sunken Temple | 29 | 4 | 86 | 53 | 33 | 0 | green |
|
||||
| Razorfen Downs | 19 | 6 | 55 | 30 | 25 | 0 | green |
|
||||
| Scarlet Monastery | 32 | 5 | 89 | 52 | 37 | 0 | green |
|
||||
| Zul'Farrak | 16 | 5 | 53 | 32 | 21 | 0 | green |
|
||||
| Blackrock Spire | 63 | 15 | 210 | 132 | 78 | 0 | green |
|
||||
| Blackrock Depths | 48 | 13 | 145 | 84 | 61 | 0 | green |
|
||||
| Scholomance | 30 | 8 | 115 | 77 | 38 | 0 | green |
|
||||
| Stratholme | 41 | 9 | 131 | 81 | 50 | 0 | green |
|
||||
| Maraudon | 25 | 8 | 83 | 50 | 33 | 0 | green |
|
||||
| Ragefire Chasm | 7 | 4 | 21 | 10 | 11 | 0 | green |
|
||||
| Dire Maul | 63 | 12 | 184 | 109 | 75 | 0 | green |
|
||||
| Hellfire Citadel: The Shattered Halls | 29 | 3 | 74 | 42 | 32 | 0 | green |
|
||||
| Hellfire Citadel: The Blood Furnace | 14 | 3 | 44 | 27 | 17 | 0 | green |
|
||||
| Hellfire Citadel: Ramparts | 10 | 2 | 36 | 24 | 12 | 0 | green |
|
||||
| Coilfang: The Steamvault | 14 | 3 | 50 | 33 | 17 | 0 | green |
|
||||
| Coilfang: The Underbog | 18 | 4 | 59 | 37 | 22 | 0 | green |
|
||||
| Coilfang: The Slave Pens | 18 | 3 | 53 | 32 | 21 | 0 | green |
|
||||
| Tempest Keep: The Arcatraz | 25 | 4 | 73 | 44 | 29 | 0 | green |
|
||||
| Tempest Keep: The Botanica | 24 | 5 | 67 | 38 | 29 | 0 | green |
|
||||
| Tempest Keep: The Mechanar | 14 | 3 | 45 | 28 | 17 | 0 | green |
|
||||
| Auchindoun: Shadow Labyrinth | 18 | 4 | 46 | 24 | 22 | 0 | green |
|
||||
| Auchindoun: Sethekk Halls | 14 | 2 | 39 | 23 | 16 | 0 | green |
|
||||
| Auchindoun: Mana-Tombs | 11 | 3 | 33 | 19 | 14 | 0 | green |
|
||||
| Auchindoun: Auchenai Crypts | 12 | 2 | 30 | 16 | 14 | 0 | green |
|
||||
| Magister's Terrace | 15 | 3 | 33 | 15 | 18 | 0 | green |
|
||||
| The Black Morass | 4 | 3 | 20 | 13 | 7 | 0 | green |
|
||||
| The Escape From Durnholde | 26 | 3 | 45 | 16 | 29 | 0 | green |
|
||||
| Utgarde Keep | 19 | 3 | 35 | 13 | 22 | 0 | green |
|
||||
| Utgarde Pinnacle | 13 | 4 | 32 | 15 | 17 | 0 | green |
|
||||
| The Nexus | 21 | 5 | 50 | 24 | 26 | 0 | green |
|
||||
| The Oculus | 8 | 4 | 27 | 15 | 12 | 0 | green |
|
||||
| The Culling of Stratholme | 16 | 4 | 41 | 21 | 20 | 0 | green |
|
||||
| Halls of Stone | 13 | 4 | 32 | 15 | 16 | 1 | green |
|
||||
| Drak'Tharon Keep | 18 | 4 | 38 | 16 | 22 | 0 | green |
|
||||
| Azjol-Nerub | 10 | 3 | 25 | 12 | 13 | 0 | green |
|
||||
| Halls of Lightning | 16 | 4 | 35 | 15 | 20 | 0 | green |
|
||||
| Gundrak | 15 | 5 | 43 | 23 | 20 | 0 | green |
|
||||
| Violet Hold | 2 | 3 | 19 | 14 | 5 | 0 | green |
|
||||
| Ahn'kahet: The Old Kingdom | 19 | 5 | 43 | 19 | 24 | 0 | green |
|
||||
| The Forge of Souls | 11 | 2 | 21 | 8 | 13 | 0 | green |
|
||||
| Trial of the Champion | 0 | 5 | 22 | 17 | 5 | 0 | green |
|
||||
| Pit of Saron | 27 | 3 | 43 | 13 | 30 | 0 | green |
|
||||
| Halls of Reflection | 8 | 2 | 26 | 16 | 10 | 0 | green |
|
||||
| Tinkertech Showdown | 3 | 1 | 8 | 4 | 0 | 4 | green |
|
||||
| Frozen Reach | 3 | 1 | 8 | 4 | 0 | 4 | green |
|
||||
| Forgotten Mine | 3 | 1 | 8 | 4 | 0 | 4 | green |
|
||||
| Wailing Caverns | 23 | 9 | 72 | 40 | 24 | 8 | green |
|
||||
| Shadowfang Keep | 24 | 7 | 65 | 34 | 31 | 0 | green |
|
||||
| Stormwind Stockade | 6 | 5 | 42 | 31 | 11 | 0 | green |
|
||||
| The Deadmines | 15 | 7 | 55 | 33 | 22 | 0 | green |
|
||||
| Razorfen Kraul | 31 | 7 | 87 | 49 | 38 | 0 | green |
|
||||
| Blackfathom Deeps | 23 | 6 | 67 | 38 | 27 | 2 | green |
|
||||
| Uldaman | 31 | 7 | 104 | 66 | 38 | 0 | green |
|
||||
| Gnomeregan | 28 | 5 | 84 | 51 | 31 | 2 | green |
|
||||
| Sunken Temple | 31 | 4 | 95 | 60 | 33 | 2 | green |
|
||||
| Razorfen Downs | 21 | 6 | 65 | 38 | 25 | 2 | green |
|
||||
| Scarlet Monastery | 32 | 5 | 107 | 70 | 37 | 0 | green |
|
||||
| Zul'Farrak | 23 | 5 | 76 | 48 | 21 | 7 | green |
|
||||
| Blackrock Spire | 66 | 17 | 285 | 202 | 78 | 5 | green |
|
||||
| Blackrock Depths | 58 | 13 | 192 | 121 | 61 | 10 | green |
|
||||
| Scholomance | 31 | 8 | 151 | 112 | 38 | 1 | green |
|
||||
| Stratholme | 45 | 9 | 180 | 126 | 50 | 4 | green |
|
||||
| Maraudon | 25 | 8 | 95 | 62 | 33 | 0 | green |
|
||||
| Ragefire Chasm | 7 | 4 | 23 | 12 | 11 | 0 | green |
|
||||
| Dire Maul | 63 | 12 | 242 | 167 | 75 | 0 | green |
|
||||
| Hellfire Citadel: The Shattered Halls | 29 | 3 | 92 | 60 | 32 | 0 | green |
|
||||
| Hellfire Citadel: The Blood Furnace | 14 | 3 | 53 | 36 | 17 | 0 | green |
|
||||
| Hellfire Citadel: Ramparts | 11 | 4 | 47 | 32 | 12 | 3 | green |
|
||||
| Coilfang: The Steamvault | 14 | 3 | 63 | 46 | 17 | 0 | green |
|
||||
| Coilfang: The Underbog | 20 | 4 | 82 | 58 | 22 | 2 | green |
|
||||
| Coilfang: The Slave Pens | 21 | 4 | 66 | 43 | 21 | 4 | green |
|
||||
| Tempest Keep: The Arcatraz | 28 | 4 | 108 | 76 | 29 | 3 | green |
|
||||
| Tempest Keep: The Botanica | 28 | 5 | 89 | 56 | 29 | 4 | green |
|
||||
| Tempest Keep: The Mechanar | 14 | 3 | 69 | 52 | 17 | 0 | green |
|
||||
| Auchindoun: Shadow Labyrinth | 18 | 4 | 67 | 45 | 22 | 0 | green |
|
||||
| Auchindoun: Sethekk Halls | 19 | 3 | 59 | 37 | 16 | 6 | green |
|
||||
| Auchindoun: Mana-Tombs | 12 | 4 | 50 | 34 | 14 | 2 | green |
|
||||
| Auchindoun: Auchenai Crypts | 13 | 3 | 48 | 32 | 14 | 2 | green |
|
||||
| Magister's Terrace | 23 | 3 | 65 | 39 | 18 | 8 | green |
|
||||
| The Black Morass | 13 | 3 | 34 | 18 | 7 | 9 | green |
|
||||
| The Escape From Durnholde | 32 | 3 | 59 | 24 | 29 | 6 | green |
|
||||
| Utgarde Keep | 19 | 6 | 55 | 30 | 22 | 3 | green |
|
||||
| Utgarde Pinnacle | 19 | 4 | 50 | 27 | 17 | 6 | green |
|
||||
| The Nexus | 24 | 5 | 64 | 35 | 26 | 3 | green |
|
||||
| The Oculus | 17 | 4 | 42 | 21 | 12 | 9 | green |
|
||||
| The Culling of Stratholme | 21 | 4 | 49 | 24 | 20 | 5 | green |
|
||||
| Halls of Stone | 16 | 4 | 45 | 25 | 16 | 4 | green |
|
||||
| Drak'Tharon Keep | 20 | 4 | 47 | 23 | 22 | 2 | green |
|
||||
| Azjol-Nerub | 18 | 3 | 38 | 17 | 13 | 8 | green |
|
||||
| Halls of Lightning | 19 | 4 | 52 | 29 | 20 | 3 | green |
|
||||
| Gundrak | 15 | 5 | 53 | 33 | 20 | 0 | green |
|
||||
| Violet Hold | 17 | 3 | 39 | 19 | 5 | 15 | green |
|
||||
| Ahn'kahet: The Old Kingdom | 23 | 5 | 64 | 36 | 24 | 4 | green |
|
||||
| The Forge of Souls | 11 | 2 | 23 | 10 | 13 | 0 | green |
|
||||
| Trial of the Champion | 6 | 12 | 123 | 105 | 5 | 13 | green |
|
||||
| Pit of Saron | 31 | 3 | 53 | 19 | 30 | 4 | green |
|
||||
| Halls of Reflection | 16 | 2 | 46 | 28 | 10 | 8 | green |
|
||||
| Tinkertech Showdown | 3 | 1 | 13 | 12 | 0 | 4 | green |
|
||||
| Frozen Reach | 2 | 1 | 19 | 19 | 0 | 3 | green |
|
||||
| Forgotten Mine | 4 | 1 | 13 | 12 | 0 | 5 | green |
|
||||
|
||||
## Documented source-faithful exceptions
|
||||
|
||||
- wailing-caverns:entry-3653: Kresh is melee-only in the pinned AzerothCore snapshot; the historical server has no spell or SmartAI row for entry 3653.
|
||||
- wailing-caverns:kresh: Kresh is melee-only in the pinned AzerothCore snapshot; the historical server has no spell or SmartAI row for entry 3653.
|
||||
- wailing-caverns:deviate-faerie-dragon: The server-faithful rare entry 5912 has no spell rows and is despawned by the Wailing Caverns initialization event.
|
||||
- trial-of-the-champion: Trial of the Champion is an encounter arena; its imported combat population is the five Grand Champion bosses, not a trash route.
|
||||
- gnomeregan:wow-classic-batch-three-entry-7361: Grubbis is melee-only in the pinned AzerothCore snapshot; entry 7361 has aggro dialogue and instance-completion rows but no combat spell cast.
|
||||
- blackrock-spire:entry-9218: Spirestone Battle Lord is a 15% rare spawn in the pinned database and has no combat spell cast beyond its basic melee attack.
|
||||
- blackrock-spire:entry-9219: Spirestone Butcher is a 15% rare spawn in the pinned database and has no combat spell cast beyond its basic melee attack.
|
||||
- blackrock-spire:entry-10376: Crystal Fang is a 15% rare spawn with no active combat spell in the pinned database; its exact on-death Spire Spiderling summon is implemented by the instance adapter.
|
||||
|
||||
## Issues
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,388 @@
|
||||
{
|
||||
"source": "patch-enUS-M.MPQ",
|
||||
"sourceSha256": "b69713077e19498d20efa5eeaefdba98850764db6bdc99e292bfbdf7b1c858d6",
|
||||
"imported": [
|
||||
{
|
||||
"id": "shadowfang-keep",
|
||||
"floors": 7,
|
||||
"checkedSpawns": 109,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "stormwind-stockade",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 93,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004000000000026205
|
||||
},
|
||||
{
|
||||
"id": "deadmines",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 179,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00005830951894693099
|
||||
},
|
||||
{
|
||||
"id": "wailing-caverns",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 251,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.188595526192965
|
||||
},
|
||||
{
|
||||
"id": "razorfen-kraul",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 179,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00006573939954203296
|
||||
},
|
||||
{
|
||||
"id": "blackfathom-deeps",
|
||||
"floors": 3,
|
||||
"checkedSpawns": 201,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "uldaman",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 302,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "gnomeregan",
|
||||
"floors": 4,
|
||||
"checkedSpawns": 371,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "sunken-temple",
|
||||
"floors": 3,
|
||||
"checkedSpawns": 244,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "razorfen-downs",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 192,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 5.084354071042896e-13
|
||||
},
|
||||
{
|
||||
"id": "scarlet-monastery",
|
||||
"floors": 4,
|
||||
"checkedSpawns": 310,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "zulfarrak",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 251,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "blackrock-spire",
|
||||
"floors": 7,
|
||||
"checkedSpawns": 565,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "blackrock-depths",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 1040,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "scholomance",
|
||||
"floors": 4,
|
||||
"checkedSpawns": 327,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00009095950916596921
|
||||
},
|
||||
{
|
||||
"id": "stratholme",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 430,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 9.096057172999542e-13
|
||||
},
|
||||
{
|
||||
"id": "maraudon",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 525,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "ragefire-chasm",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 127,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "dire-maul",
|
||||
"floors": 6,
|
||||
"checkedSpawns": 755,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00006403124240144967
|
||||
},
|
||||
{
|
||||
"id": "shattered-halls",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 198,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00006403124237426248
|
||||
},
|
||||
{
|
||||
"id": "blood-furnace",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 139,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00006403124237384636
|
||||
},
|
||||
{
|
||||
"id": "hellfire-ramparts",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 85,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00006753673068807912
|
||||
},
|
||||
{
|
||||
"id": "steamvault",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 127,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "underbog",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 161,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.0000617170964964256
|
||||
},
|
||||
{
|
||||
"id": "slave-pens",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 122,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.0000640312423744012
|
||||
},
|
||||
{
|
||||
"id": "arcatraz",
|
||||
"floors": 3,
|
||||
"checkedSpawns": 84,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "botanica",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 171,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00006403124237384636
|
||||
},
|
||||
{
|
||||
"id": "mechanar",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 84,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.000046699999999996744
|
||||
},
|
||||
{
|
||||
"id": "shadow-labyrinth",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 189,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00006403124237426248
|
||||
},
|
||||
{
|
||||
"id": "sethekk-halls",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 104,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00005324368507145524
|
||||
},
|
||||
{
|
||||
"id": "mana-tombs",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 119,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.000056568542495137386
|
||||
},
|
||||
{
|
||||
"id": "auchenai-crypts",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 93,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00005415025392387915
|
||||
},
|
||||
{
|
||||
"id": "magisters-terrace",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 147,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00007071067811848996
|
||||
},
|
||||
{
|
||||
"id": "black-morass",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 134,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "escape-from-durnholde",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 380,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "utgarde-keep",
|
||||
"floors": 3,
|
||||
"checkedSpawns": 112,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004700000000001925
|
||||
},
|
||||
{
|
||||
"id": "utgarde-pinnacle",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 83,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "the-nexus",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 234,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "the-oculus",
|
||||
"floors": 5,
|
||||
"checkedSpawns": 112,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "culling-of-stratholme",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 209,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "halls-of-stone",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 76,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "draktharon-keep",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 99,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "azjol-nerub",
|
||||
"floors": 3,
|
||||
"checkedSpawns": 70,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "halls-of-lightning",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 112,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "gundrak",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 85,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "violet-hold",
|
||||
"floors": 2,
|
||||
"checkedSpawns": 8,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "ahnkahet-old-kingdom",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 133,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004999999999988347
|
||||
},
|
||||
{
|
||||
"id": "forge-of-souls",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 41,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "trial-of-the-champion",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 0,
|
||||
"checkedEntrances": 1,
|
||||
"maxAlignmentError": 0
|
||||
},
|
||||
{
|
||||
"id": "pit-of-saron",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 167,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0.00004000000000026205
|
||||
},
|
||||
{
|
||||
"id": "halls-of-reflection",
|
||||
"floors": 1,
|
||||
"checkedSpawns": 13,
|
||||
"checkedEntrances": 0,
|
||||
"maxAlignmentError": 0
|
||||
}
|
||||
],
|
||||
"skipped": [
|
||||
{
|
||||
"id": "tinkertech-showdown",
|
||||
"reason": "No matching WorldMapArea record"
|
||||
},
|
||||
{
|
||||
"id": "frozen-reach",
|
||||
"reason": "No matching WorldMapArea record"
|
||||
},
|
||||
{
|
||||
"id": "forgotten-mine",
|
||||
"reason": "No matching WorldMapArea record"
|
||||
}
|
||||
],
|
||||
"coordinateSources": [
|
||||
"WorldMapArea.dbc",
|
||||
"DungeonMap.dbc"
|
||||
],
|
||||
"layoutSource": "https://github.com/wowgaming/3.3.5-interface-files/blob/main/WorldMapFrame.xml",
|
||||
"archives": [
|
||||
"patch-enUS-M.MPQ",
|
||||
"patch-enUS-3.MPQ",
|
||||
"patch-enUS.MPQ",
|
||||
"patch-enUS-2.MPQ"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
# WoW dungeon lighting and stair review - 2026-09-13
|
||||
|
||||
## Implemented coverage
|
||||
|
||||
All 54 campaign WoW dungeons now receive environment-specific lighting using
|
||||
Stockades' reviewed ambient/hemisphere fill as the baseline. Stone, cavern,
|
||||
fire, arcane, crypt, and outdoor profiles preserve atmosphere without applying
|
||||
the old dark expansion palettes. Stockades' existing presentation and three
|
||||
reviewed stair proxies remain unchanged. RuneWaker environments are excluded.
|
||||
|
||||
The fleet scan produced 347 additional collision-verified walking proxies in
|
||||
22 dungeons, and eight filled Recast boundary bridges in Uldaman. This makes
|
||||
350 walking proxies including the three existing Stockades flights. Visual
|
||||
stairs remain steps: source visual/collision/navigation GLBs were not modified.
|
||||
The shared prepared collision is used by players, companions, and enemies.
|
||||
|
||||
| Dungeon | Additional walking proxies | Additional navigation bridges |
|
||||
| --- | ---: | ---: |
|
||||
| Shadowfang Keep | 41 | 0 |
|
||||
| Blackfathom Deeps | 5 | 0 |
|
||||
| Uldaman | 5 | 8 |
|
||||
| Gnomeregan | 4 | 0 |
|
||||
| Sunken Temple | 2 | 0 |
|
||||
| Scarlet Monastery | 37 | 0 |
|
||||
| Zul'Farrak | 1 | 0 |
|
||||
| Scholomance | 36 | 0 |
|
||||
| Stratholme | 12 | 0 |
|
||||
| Blood Furnace | 1 | 0 |
|
||||
| Shadow Labyrinth | 1 | 0 |
|
||||
| Mana-Tombs | 3 | 0 |
|
||||
| Auchenai Crypts | 5 | 0 |
|
||||
| Magister's Terrace | 10 | 0 |
|
||||
| Escape From Durnholde | 120 | 0 |
|
||||
| Utgarde Keep | 8 | 0 |
|
||||
| Utgarde Pinnacle | 32 | 0 |
|
||||
| Culling of Stratholme | 11 | 0 |
|
||||
| Halls of Stone | 2 | 0 |
|
||||
| Halls of Lightning | 1 | 0 |
|
||||
| Pit of Saron | 1 | 0 |
|
||||
| Halls of Reflection | 9 | 0 |
|
||||
|
||||
The machine-readable `src/game/generated/wowDungeonStairs.generated.json`
|
||||
records a scan for every WoW dungeon, including entries with no accepted new
|
||||
proxy, collision package identities, candidate/rejection counts, and exact
|
||||
navigation portals. Authored Wailing Caverns is scanned against its actual
|
||||
source assets, rather than the campaign's separate canonical package.
|
||||
|
||||
## Safety and verification
|
||||
|
||||
- Require repeated short risers with consistent rise/run and overlapping width.
|
||||
- Merge compatible continuous flights; independently verify short fallbacks
|
||||
when a merged flight is unsafe.
|
||||
- Rebuild after rejected proxies are removed, so accepted paths do not depend
|
||||
on rejected geometry edits.
|
||||
- Check real terrain support and capsule sweeps uphill/downhill along the center
|
||||
and two interior lateral lanes at approximately 0.35m intervals.
|
||||
- Fill only separate existing boundary portals wholly within a verified flight.
|
||||
Do not invent off-mesh links across unrelated floors or walls.
|
||||
- Gate generated coordinates by collision package URL/checksum identity, and
|
||||
navigation identity where exact portal bridges are present.
|
||||
- Keep existing validation/availability gates unchanged. Stair checks do not
|
||||
certify boss routes, encounters, resets, checkpoints, or complete runs.
|
||||
- Spatially index ramp footprints during preparation to avoid checking every
|
||||
ramp against every vertex in large, multi-chunk environments.
|
||||
|
||||
The shipping-asset regression tests independently exercise all 347 new proxies
|
||||
through `prepareCollision` and the live NPC direct-segment validator in both
|
||||
directions and all three tested lanes. All generated portals match the actual
|
||||
shipping navigation vertices. Existing Stockades player/NPC/wall regressions pass.
|
||||
|
||||
Full validation: 247 Vitest files passed, 1,599 tests passed, six existing tests
|
||||
skipped. Production TypeScript/Vite build, texture/performance audits, bundle
|
||||
compression/verification, and release candidate checking passed. Existing
|
||||
physics initialization and bundle-size/import advisories remain.
|
||||
|
||||
## Visual evidence and remaining QA
|
||||
|
||||
The local visual viewer uses the runtime definitions/material policy, shipped
|
||||
GLBs, Meshopt/KTX2 decoding, ACES tone mapping, and 1280x720 screenshots. It does
|
||||
not modify saved characters or party state. Evidence is under
|
||||
`artifacts/wow-dungeon-environment/` (ignored QA artifacts).
|
||||
|
||||
Uldaman's new/old-fill comparison shows clearer stone walls, columns, and floor
|
||||
tiles. Wailing Caverns retains green coloration with readable rock formations.
|
||||
Mechanar retains cool purple/blue interiors and unchanged visible steps.
|
||||
Scholomance's entrance stairs and masonry remain visible with the crypt profile.
|
||||
Blood Furnace's dark source textures remain atmospheric; the entrance viewpoint
|
||||
faces a nearby wall, so this is not a full-room traversal review.
|
||||
|
||||
Zul'Farrak showed a geometry/viewpoint gap in the standalone entrance and boss
|
||||
views. Do not treat its lighting profile or passing isolated proxy sweep as an
|
||||
end-to-end visual/playability pass. Durnholde's large package completed loading
|
||||
without console errors; its cavern entrance and outdoor fort masonry/trees were
|
||||
readable, but the boss viewpoint also showed missing terrain/viewpoint coverage.
|
||||
That environment needs a separate terrain/spawn review and live traversal check.
|
||||
|
||||
This is not an exhaustive certification that every staircase is repaired.
|
||||
Conservative repeated-riser detection can miss curved flights, one/two-step
|
||||
transitions, and heavily decimated source geometry. A rejected candidate may be
|
||||
decorative, lack headroom/ground, or require an individually reviewed footprint.
|
||||
An entry with no new proxy is not proof that it contains no stairs. Full-dungeon
|
||||
actor traversal, entrance/exit lips, outer stair edges, manual review of rejected
|
||||
or undetected flights, full clears, and physical Android QA remain open.
|
||||
|
||||
## Repeating the checks
|
||||
|
||||
Requires the project's Node 24 runtime for native TypeScript pipeline execution.
|
||||
|
||||
```text
|
||||
npm run dungeon:wow:stairs:audit
|
||||
npm run dungeon:wow:stairs:refresh
|
||||
npm run dungeon:wow:stairs:audit -- scholomance --diagnostics
|
||||
node scripts/dungeon-pipeline/audit-wow-stairs.ts wailing-caverns --update
|
||||
npm test -- --run src/game/wowDungeonStairAssets.test.ts
|
||||
```
|
||||
|
||||
The audit is read-only by default. Refresh writes the full generated catalog;
|
||||
`--update` replaces only the named entry while retaining the other entries.
|
||||
Campaign regeneration does not erase the runtime lighting/stair adapter.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,858 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"scope": "ascension-custom-five-player-source-recovery",
|
||||
"status": "partial-authoritative-recovery",
|
||||
"sourcePolicy": {
|
||||
"exact": "Values copied from installed-client DBC rows or already checksummed map packages.",
|
||||
"spellLinked": "Creature and gameobject IDs recovered from summon effects in installed-client Spell.dbc. The link is exact even when the missing creature_template or gameobject_template name is inferred from the spell context.",
|
||||
"authored": "Timing, phase ownership, placement, health tuning, and any entity explicitly marked authored are adaptation decisions, not claims about Ascension server behavior."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"id": "ascension-area52-spell-dbc",
|
||||
"kind": "installed-client-dbc",
|
||||
"pathHint": "../LadiksMPQEditor/client-current/area-52/patch-D/DBFilesClient/Spell.dbc",
|
||||
"sha256": "2f77f18b91ffbae32a19568e4bc05ec78ee384b6ba8dd2a6e05f0e97abbb2dce",
|
||||
"header": {
|
||||
"signature": "WDBC",
|
||||
"recordCount": 238891,
|
||||
"fieldCount": 234,
|
||||
"recordSize": 936,
|
||||
"stringBlockSize": 14058299
|
||||
},
|
||||
"localeField": "enUS",
|
||||
"confidence": "exact"
|
||||
},
|
||||
{
|
||||
"id": "manastorm-runtime-catalog",
|
||||
"kind": "installed-client-encounter-export",
|
||||
"path": "src/game/generated/manastormRuntimeCatalog.json",
|
||||
"confidence": "exact"
|
||||
},
|
||||
{
|
||||
"id": "manastorm-asset-packages",
|
||||
"kind": "installed-client-map-export",
|
||||
"path": "src/game/generated/manastormAssetPackages.json",
|
||||
"confidence": "exact-environment-provisional-semantics"
|
||||
},
|
||||
{
|
||||
"id": "campaign-runtime-snapshot",
|
||||
"kind": "generated-runtime-catalog",
|
||||
"path": "src/game/generated/dungeonCampaignCatalog.json",
|
||||
"confidence": "exact-current-state"
|
||||
},
|
||||
{
|
||||
"id": "ascension-db-alva",
|
||||
"kind": "official-public-database",
|
||||
"url": "https://db.ascension.gg/?npc=97700",
|
||||
"confidence": "exact"
|
||||
},
|
||||
{
|
||||
"id": "ascension-db-arktos",
|
||||
"kind": "official-public-database",
|
||||
"url": "https://db.ascension.gg/?npc=97701",
|
||||
"confidence": "exact"
|
||||
},
|
||||
{
|
||||
"id": "ascension-changelog-frozen-reach-blast-arrow",
|
||||
"kind": "official-changelog",
|
||||
"url": "https://ascension.gg/en/changelog/1?page=7",
|
||||
"confidence": "exact-wording-name-differs"
|
||||
},
|
||||
{
|
||||
"id": "ascension-changelog-frozen-reach-rebirth",
|
||||
"kind": "official-changelog",
|
||||
"url": "https://ascension.gg/en/changelog/1?page=8",
|
||||
"confidence": "exact"
|
||||
},
|
||||
{
|
||||
"id": "ascension-changelog-forgotten-mine-crystals",
|
||||
"kind": "official-changelog",
|
||||
"url": "https://ascension.gg/en/changelog/1?page=51",
|
||||
"confidence": "exact"
|
||||
},
|
||||
{
|
||||
"id": "ascension-changelog-forgotten-mine-geospike",
|
||||
"kind": "official-changelog",
|
||||
"url": "https://ascension.gg/en/changelog/1?page=29",
|
||||
"confidence": "exact"
|
||||
}
|
||||
],
|
||||
"localSearchAudit": {
|
||||
"searchedRoots": [
|
||||
"../wow-pvt-server-backups-2",
|
||||
"../WoW-Pvt-Server-Backups",
|
||||
"../wow-private-server-notes",
|
||||
"../LadiksMPQEditor",
|
||||
"../wow335a/Cache",
|
||||
"../resources/client/Cache"
|
||||
],
|
||||
"searchedArtifacts": [
|
||||
"creature_template and creature spawn SQL",
|
||||
"gameobject_template and gameobject spawn SQL",
|
||||
"SmartAI and ScriptName bindings",
|
||||
"Lua/XML/JSON/CSV encounter data",
|
||||
"creaturecache.wdb and gameobjectcache.wdb records"
|
||||
],
|
||||
"missingAuthoritativeData": [
|
||||
"creature templates and display mappings for 663012, 97700, and 254272",
|
||||
"world spawn rows for maps 821, 908, and 1781",
|
||||
"server-side encounter scripts and cast cadence",
|
||||
"authoritative trash population and patrol routes",
|
||||
"authoritative gameobject placements"
|
||||
],
|
||||
"publicSnapshotCheck": {
|
||||
"repository": "https://github.com/Ascension-Addons/Questie-X-AscensionDB.git",
|
||||
"commit": "cf6710ef94c264d842cf2ea94c4b50e9ece6dd61",
|
||||
"result": "No target boss IDs, names, spawns, or encounter scripts were present."
|
||||
}
|
||||
},
|
||||
"dungeons": [
|
||||
{
|
||||
"mapId": 821,
|
||||
"id": "tinkertech-showdown",
|
||||
"title": "Tinkertech Showdown",
|
||||
"mapDirectory": "TinketechShowdown",
|
||||
"sourceClassification": "identity-and-environment-only",
|
||||
"environment": {
|
||||
"sourceHash": "323c47c081d0a6c39daf9878284863c21e3e38dbced2ba3f58d1d157d080d41d",
|
||||
"rootWmo": "World/wmo/Dungeon/AZ_Subway/Subway_Dungeon.wmo",
|
||||
"anchors": {
|
||||
"player": [
|
||||
3.648781,
|
||||
-4.167801,
|
||||
66.263252
|
||||
],
|
||||
"trash": [
|
||||
10.220209,
|
||||
-126.067818,
|
||||
1266.206177
|
||||
],
|
||||
"boss": [
|
||||
-15.779793,
|
||||
-4.267807,
|
||||
2439.406006
|
||||
]
|
||||
},
|
||||
"semanticStatus": "provisional-geometry-derived"
|
||||
},
|
||||
"encounters": [
|
||||
{
|
||||
"encounterId": 9013,
|
||||
"modeId": 2,
|
||||
"name": "Mechatron",
|
||||
"creatureId": 663012,
|
||||
"identityConfidence": "exact"
|
||||
}
|
||||
],
|
||||
"currentRuntime": {
|
||||
"bossEntityId": "entry-663012",
|
||||
"proceduralTrashSpawnCount": 15,
|
||||
"proceduralMobEntries": [
|
||||
{
|
||||
"entityId": "tinkertech-showdown-guardian",
|
||||
"serverEntry": 2629079128
|
||||
},
|
||||
{
|
||||
"entityId": "tinkertech-showdown-caster",
|
||||
"serverEntry": 3849447491
|
||||
},
|
||||
{
|
||||
"entityId": "tinkertech-showdown-brute",
|
||||
"serverEntry": 1787979165
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceRecovery": {
|
||||
"association": "candidate-client-cluster-only",
|
||||
"associationConfidence": "low",
|
||||
"reason": "These adjacent client rows form a coherent mechanical-machine kit and include Sound: Goblin Head Machine Loop, but no recovered server row links them to creature 663012.",
|
||||
"spellRows": {
|
||||
"257438": "Rocket",
|
||||
"257459": "Gatling Missile Barrage",
|
||||
"257460": "Gatling Missile",
|
||||
"257461": "Gatling Missile",
|
||||
"257462": "Sound: Goblin Head Machine Loop",
|
||||
"257470": "Incineration Beam",
|
||||
"257471": "Energy Ball",
|
||||
"257472": "Electrified Zone",
|
||||
"257473": "Electric Shock"
|
||||
},
|
||||
"families": {
|
||||
"rocket": [
|
||||
257438
|
||||
],
|
||||
"gatlingMissileBarrage": [
|
||||
257459,
|
||||
257460,
|
||||
257461
|
||||
],
|
||||
"incinerationBeam": [
|
||||
257470
|
||||
],
|
||||
"electrifiedZone": [
|
||||
257472,
|
||||
257473
|
||||
]
|
||||
}
|
||||
},
|
||||
"adaptation": {
|
||||
"status": "custom-authoring-required",
|
||||
"populationPlan": {
|
||||
"disposition": "retain-current-route-as-explicit-adaptation",
|
||||
"packCount": 5,
|
||||
"membersPerPack": 3,
|
||||
"authoredMobs": [
|
||||
{
|
||||
"serverEntry": 2629079128,
|
||||
"name": "Tinkertech Guardbot",
|
||||
"role": "melee-interceptor"
|
||||
},
|
||||
{
|
||||
"serverEntry": 3849447491,
|
||||
"name": "Tinkertech Coilcaster",
|
||||
"role": "electrical-ranged"
|
||||
},
|
||||
{
|
||||
"serverEntry": 1787979165,
|
||||
"name": "Tinkertech Demolisher",
|
||||
"role": "rocket-bruiser"
|
||||
}
|
||||
]
|
||||
},
|
||||
"encounterPlan": [
|
||||
"Mechatron remains creature 663012 at the provisional boss anchor.",
|
||||
"Rocket uses a 2.0 second target reticle and splash damage on an authored 8 second cadence.",
|
||||
"Gatling Missile Barrage uses a 3.0 second warning followed by eight lane impacts over 4 seconds on an authored 14 second cadence.",
|
||||
"Incineration Beam uses a 2.5 second wind-up and sweeps the subway lane for 5 seconds; immutable web geometry means it visually disables lane sections instead of permanently destroying client platforms.",
|
||||
"Electrified Zone alternates floor strips after a 1.5 second warning, remains active for 8 seconds, and applies a 1.5 second stun on contact.",
|
||||
"At 35 percent health, cadence accelerates by 20 percent and two authored Coilcasters enter from the previous pack anchor."
|
||||
],
|
||||
"mustBeCustomAuthored": [
|
||||
"all trash identities, placements, and AI",
|
||||
"all boss timing and phase thresholds",
|
||||
"the decision to associate candidate spells 257438 and 257459-257473 with Mechatron",
|
||||
"platform or lane disable behavior",
|
||||
"boss and trash visual composites",
|
||||
"loot and completion scripting"
|
||||
]
|
||||
},
|
||||
"unresolved": [
|
||||
"No authoritative Mechatron spell-to-creature binding was found.",
|
||||
"No authoritative trash or gameobject IDs were found.",
|
||||
"The custom ADT placement transform and all semantic anchors still require playtest approval."
|
||||
]
|
||||
},
|
||||
{
|
||||
"mapId": 908,
|
||||
"id": "frozen-reach",
|
||||
"title": "Frozen Reach",
|
||||
"mapDirectory": "Alvaencounter",
|
||||
"sourceClassification": "encounter-spells-and-summons-recovered",
|
||||
"environment": {
|
||||
"sourceHash": "5ab771df79c0e062cea3ebda4e416a9245bcf8a4cc6da048077df5e674081f97",
|
||||
"anchors": {
|
||||
"player": [
|
||||
1858.534668,
|
||||
78.860199,
|
||||
2229.334473
|
||||
],
|
||||
"trash": [
|
||||
2100.134766,
|
||||
80.860199,
|
||||
2132.93457
|
||||
],
|
||||
"boss": [
|
||||
2344.134766,
|
||||
62.460205,
|
||||
2134.334717
|
||||
]
|
||||
},
|
||||
"semanticStatus": "provisional-elevated-encounter-component"
|
||||
},
|
||||
"encounters": [
|
||||
{
|
||||
"encounterId": 2168,
|
||||
"modeId": 0,
|
||||
"name": "Alva",
|
||||
"creatureId": 97700,
|
||||
"identityConfidence": "exact"
|
||||
},
|
||||
{
|
||||
"encounterId": 2170,
|
||||
"modeId": 2,
|
||||
"name": "Alva",
|
||||
"creatureId": 97700,
|
||||
"identityConfidence": "exact"
|
||||
}
|
||||
],
|
||||
"entities": [
|
||||
{
|
||||
"entryId": 97700,
|
||||
"name": "Alva",
|
||||
"role": "boss",
|
||||
"sourceKind": "encounter-dbc-and-official-db"
|
||||
},
|
||||
{
|
||||
"entryId": 97701,
|
||||
"name": "Arktos",
|
||||
"role": "boss-companion",
|
||||
"sourceKind": "official-db"
|
||||
},
|
||||
{
|
||||
"entryId": 97703,
|
||||
"name": "Blast Arrow summoned unit",
|
||||
"role": "area-trigger",
|
||||
"sourceKind": "spell-linked-name-inferred"
|
||||
},
|
||||
{
|
||||
"entryId": 97731,
|
||||
"name": "Explosive Trap summoned unit",
|
||||
"role": "trap",
|
||||
"sourceKind": "spell-linked-name-inferred"
|
||||
},
|
||||
{
|
||||
"entryId": 97770,
|
||||
"name": "Alvajar summoned unit",
|
||||
"role": "add",
|
||||
"sourceKind": "spell-linked-name-inferred"
|
||||
}
|
||||
],
|
||||
"currentRuntime": {
|
||||
"bossEntityId": "entry-97700",
|
||||
"proceduralTrashSpawnCount": 15,
|
||||
"proceduralMobEntries": [
|
||||
{
|
||||
"entityId": "frozen-reach-guardian",
|
||||
"serverEntry": 1606324391
|
||||
},
|
||||
{
|
||||
"entityId": "frozen-reach-caster",
|
||||
"serverEntry": 854807736
|
||||
},
|
||||
{
|
||||
"entityId": "frozen-reach-brute",
|
||||
"serverEntry": 2855026892
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceRecovery": {
|
||||
"association": "explicit-alva-arktos-client-cluster",
|
||||
"associationConfidence": "high",
|
||||
"spellRows": {
|
||||
"255347": "Auto Shot",
|
||||
"255349": "Rain of Arrows",
|
||||
"255351": "Rain of Arrows",
|
||||
"255352": "Unbridled Arrows",
|
||||
"255353": "Unbridled Arrows",
|
||||
"255358": "Blast Arrow",
|
||||
"255359": "Blast Arrow",
|
||||
"255360": "Pact of the Hunt",
|
||||
"255362": "Blast Arrow",
|
||||
"255363": "Blast Arrow",
|
||||
"255374": "Ice Rock",
|
||||
"255375": "Ice Rock",
|
||||
"255377": "Ice Rock",
|
||||
"255378": "Unholy Frenzy",
|
||||
"255379": "Carnivorous Bite",
|
||||
"255380": "Frost Shield",
|
||||
"255381": "Set Ablaze",
|
||||
"255382": "Blaze",
|
||||
"255387": "Frost Strike",
|
||||
"255389": "Shadow Shroud Surge",
|
||||
"255390": "Shadow Shroud Rebirth",
|
||||
"255393": "Sphere of Grief and Wrath",
|
||||
"255394": "Vengeful Bolt",
|
||||
"255395": "Vengeful Bolt",
|
||||
"255397": "Shadow Veil Lance",
|
||||
"255398": "Shadow Veil Lance",
|
||||
"255399": "Frost Roar",
|
||||
"255401": "Cluster Shot",
|
||||
"255402": "Cluster Shot",
|
||||
"255403": "Cluster Shot",
|
||||
"255410": "Alva's Grief",
|
||||
"255411": "Alva's Grief",
|
||||
"255412": "Frost orb",
|
||||
"255413": "Frozen Pulse",
|
||||
"255417": "Shield Wall",
|
||||
"255418": "Shield Wall",
|
||||
"255419": "Alva's Wrath",
|
||||
"255420": "Throw Explosive Trap",
|
||||
"255421": "Explosive Trap",
|
||||
"255423": "Trap Explosion",
|
||||
"255424": "Charging",
|
||||
"255426": "Explosive Trap",
|
||||
"255427": "Frostblade Strike",
|
||||
"255428": "Frostblade Strike",
|
||||
"255432": "Army of the Dead",
|
||||
"255433": "Army of the Dead",
|
||||
"255434": "Lacerate",
|
||||
"255435": "Lacerate",
|
||||
"255436": "Lacerate",
|
||||
"255437": "Arcing Slice",
|
||||
"255438": "Rain of Arrows",
|
||||
"255439": "Rain of Arrows",
|
||||
"255440": "Cluster Shot",
|
||||
"255441": "Cluster Shot",
|
||||
"255442": "Unbridled Arrows",
|
||||
"255443": "Unbridled Arrows",
|
||||
"255444": "Blast Arrow",
|
||||
"255445": "Blast Arrow",
|
||||
"255446": "Frost Roar",
|
||||
"255447": "Frost Roar",
|
||||
"255448": "Vengeful Bolt",
|
||||
"255449": "Vengeful Bolt",
|
||||
"255450": "Ice Rock",
|
||||
"255451": "Ice Rock",
|
||||
"255452": "Frostblade Strike",
|
||||
"255453": "Frostblade Strike",
|
||||
"255454": "Shadow Shroud Surge",
|
||||
"255455": "Shadow Shroud Surge",
|
||||
"255456": "Shadow Shroud Rebirth",
|
||||
"255457": "Shadow Shroud Rebirth",
|
||||
"255458": "Shadow Veil Lance",
|
||||
"255459": "Shadow Veil Lance",
|
||||
"255460": "Alva's Wrath",
|
||||
"255461": "Alva's Wrath",
|
||||
"255462": "Sphere of Grief and Wrath",
|
||||
"255463": "Sphere of Grief and Wrath",
|
||||
"255464": "Frost Strike",
|
||||
"255465": "Frost Strike",
|
||||
"255466": "Predator's Fury",
|
||||
"255467": "Alva's Grief",
|
||||
"255468": "Alva's Grief",
|
||||
"255469": "Trap Explosion",
|
||||
"255470": "Trap Explosion",
|
||||
"255471": "Frozen Pulse",
|
||||
"255472": "Frozen Pulse"
|
||||
},
|
||||
"families": {
|
||||
"alvaRanged": [
|
||||
255347,
|
||||
255349,
|
||||
255351,
|
||||
255352,
|
||||
255353,
|
||||
255358,
|
||||
255359,
|
||||
255401,
|
||||
255402,
|
||||
255403,
|
||||
255420,
|
||||
255421,
|
||||
255423,
|
||||
255424
|
||||
],
|
||||
"arktos": [
|
||||
255360,
|
||||
255374,
|
||||
255375,
|
||||
255377,
|
||||
255378,
|
||||
255379,
|
||||
255380,
|
||||
255381,
|
||||
255382,
|
||||
255387,
|
||||
255399,
|
||||
255417,
|
||||
255418,
|
||||
255434
|
||||
],
|
||||
"alvaVengeance": [
|
||||
255389,
|
||||
255390,
|
||||
255393,
|
||||
255394,
|
||||
255395,
|
||||
255397,
|
||||
255398,
|
||||
255410,
|
||||
255411,
|
||||
255419,
|
||||
255432,
|
||||
255433
|
||||
],
|
||||
"difficultyOneAndTwoVariants": [
|
||||
255435,
|
||||
255436,
|
||||
255438,
|
||||
255439,
|
||||
255440,
|
||||
255441,
|
||||
255442,
|
||||
255443,
|
||||
255444,
|
||||
255445,
|
||||
255446,
|
||||
255447,
|
||||
255448,
|
||||
255449,
|
||||
255450,
|
||||
255451,
|
||||
255452,
|
||||
255453,
|
||||
255454,
|
||||
255455,
|
||||
255456,
|
||||
255457,
|
||||
255458,
|
||||
255459,
|
||||
255460,
|
||||
255461,
|
||||
255462,
|
||||
255463,
|
||||
255464,
|
||||
255465,
|
||||
255467,
|
||||
255468,
|
||||
255469,
|
||||
255470,
|
||||
255471,
|
||||
255472
|
||||
]
|
||||
},
|
||||
"summonLinks": [
|
||||
{
|
||||
"spellId": 255363,
|
||||
"effectIndex": 0,
|
||||
"effectType": 28,
|
||||
"targetKind": "creature",
|
||||
"entryId": 97703
|
||||
},
|
||||
{
|
||||
"spellId": 255421,
|
||||
"effectIndex": 0,
|
||||
"effectType": 28,
|
||||
"targetKind": "creature",
|
||||
"entryId": 97731
|
||||
},
|
||||
{
|
||||
"spellId": 255433,
|
||||
"effectIndex": 0,
|
||||
"effectType": 28,
|
||||
"targetKind": "creature",
|
||||
"entryId": 97770
|
||||
}
|
||||
]
|
||||
},
|
||||
"adaptation": {
|
||||
"status": "source-backed-custom-cadence-required",
|
||||
"populationPlan": {
|
||||
"disposition": "remove-unverified-generic-trash",
|
||||
"reason": "Alvaencounter and the recovered spell cluster describe a contained Alva/Arktos encounter; no evidence supports the current 15 generic hallway creatures.",
|
||||
"encounterEntities": [
|
||||
97700,
|
||||
97701,
|
||||
97703,
|
||||
97731,
|
||||
97770
|
||||
]
|
||||
},
|
||||
"encounterPlan": [
|
||||
"Phase one starts Alva 97700 and Arktos 97701 together, with Pact of the Hunt 255360 active while they remain close.",
|
||||
"Alva uses Rain of Arrows, Unbridled Arrows, Blast Arrow, Cluster Shot, and summoned Explosive Traps; Arktos uses Carnivorous Bite, Ice Rock, Frost Roar, Lacerate, and defensive Frost Shield or Shield Wall.",
|
||||
"Blast Arrow can strike Arktos and apply Set Ablaze 255381, causing panic movement, Blaze 255382, and increased damage taken. This matches the official changelog's Blast Shot/Arktos interaction despite the DBC naming it Blast Arrow.",
|
||||
"Arktos death leaves Alva's Grief 255410 around the corpse and transitions Alva to the vengeance kit.",
|
||||
"The transition casts Shadow Shroud Rebirth 255390 and summons four Alvajar 97770 around the arena. Rebirth damage must scale by living group size, reaching full value at five or more players per the official changelog.",
|
||||
"Phase two rotates Vengeful Bolt, Shadow Veil Lance, Sphere of Grief and Wrath, Army of the Dead, and stacking Alva's Wrath as the soft enrage.",
|
||||
"Wire ASC D0 rows to the current normal experience. Preserve D1 and D2 rows but do not assign public difficulty labels until an authoritative mode mapping is recovered."
|
||||
],
|
||||
"authoredCadence": {
|
||||
"rainOfArrowsMs": 12000,
|
||||
"unbridledArrowsMs": 20000,
|
||||
"blastArrowMs": 15000,
|
||||
"clusterShotMs": 18000,
|
||||
"explosiveTrapMs": 16000,
|
||||
"arktosIceRockMs": 12000,
|
||||
"arktosFrostRoarMs": 18000,
|
||||
"vengefulBoltMs": 7000,
|
||||
"shadowVeilLanceMs": 12000,
|
||||
"sphereOfGriefMs": 20000,
|
||||
"alvasWrathMs": 25000
|
||||
},
|
||||
"mustBeCustomAuthored": [
|
||||
"all cast cadence and target-selection rules",
|
||||
"Arktos initial placement and companion pathing",
|
||||
"phase transition and exact death threshold behavior",
|
||||
"Alvajar formation and spawn placement",
|
||||
"trap and area-trigger web-runtime behavior",
|
||||
"boss and add visual composites",
|
||||
"loot and completion scripting"
|
||||
]
|
||||
},
|
||||
"unresolved": [
|
||||
"The server-side spell schedule and encounter script are absent.",
|
||||
"Creature template stats, display IDs, and exact positions are absent.",
|
||||
"ASC D0/D1/D2 spell ranks cannot be mapped safely beyond D0 without a surviving difficulty script."
|
||||
]
|
||||
},
|
||||
{
|
||||
"mapId": 1781,
|
||||
"id": "forgotten-mine",
|
||||
"title": "Forgotten Mine",
|
||||
"mapDirectory": "ForgottenMine",
|
||||
"sourceClassification": "encounter-spells-creature-summon-and-gameobject-recovered",
|
||||
"environment": {
|
||||
"sourceHash": "693b7067aa1109e14beb944bc95aed8299bf64e2ec6ae3dc43f4fab7d18f57b6",
|
||||
"anchors": {
|
||||
"player": [
|
||||
1285.886108,
|
||||
149.58075,
|
||||
1724.264526
|
||||
],
|
||||
"trash": [
|
||||
1857.461304,
|
||||
136.080749,
|
||||
1871.884277
|
||||
],
|
||||
"boss": [
|
||||
2246.06665,
|
||||
198.180786,
|
||||
2147.866699
|
||||
]
|
||||
},
|
||||
"semanticStatus": "provisional-main-navigation-component"
|
||||
},
|
||||
"encounters": [
|
||||
{
|
||||
"encounterId": 5069,
|
||||
"modeId": 0,
|
||||
"name": "Geolord Vek",
|
||||
"creatureId": 254272,
|
||||
"identityConfidence": "exact"
|
||||
},
|
||||
{
|
||||
"encounterId": 5071,
|
||||
"modeId": 2,
|
||||
"name": "Geolord Vek",
|
||||
"creatureId": 254272,
|
||||
"identityConfidence": "exact"
|
||||
}
|
||||
],
|
||||
"entities": [
|
||||
{
|
||||
"entryId": 254272,
|
||||
"name": "Geolord Vek",
|
||||
"role": "boss",
|
||||
"sourceKind": "encounter-dbc"
|
||||
},
|
||||
{
|
||||
"entryId": 254278,
|
||||
"name": "Geospike summoned unit",
|
||||
"role": "hazard",
|
||||
"sourceKind": "spell-linked-name-inferred"
|
||||
}
|
||||
],
|
||||
"gameObjects": [
|
||||
{
|
||||
"entryId": 254051,
|
||||
"name": "Resonite Shard",
|
||||
"role": "clickable-encounter-countermeasure",
|
||||
"sourceKind": "spell-linked-name-from-create-spell"
|
||||
}
|
||||
],
|
||||
"currentRuntime": {
|
||||
"bossEntityId": "entry-254272",
|
||||
"proceduralTrashSpawnCount": 15,
|
||||
"proceduralMobEntries": [
|
||||
{
|
||||
"entityId": "forgotten-mine-guardian",
|
||||
"serverEntry": 4175632753
|
||||
},
|
||||
{
|
||||
"entityId": "forgotten-mine-caster",
|
||||
"serverEntry": 647988074
|
||||
},
|
||||
{
|
||||
"entityId": "forgotten-mine-brute",
|
||||
"serverEntry": 2677742134
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceRecovery": {
|
||||
"association": "explicit-vek-client-cluster",
|
||||
"associationConfidence": "high",
|
||||
"reason": "The contiguous cluster contains Vek Death and Vek Reset control rows alongside the complete earth and Resonite kit.",
|
||||
"spellRows": {
|
||||
"354000": "Stonebreaker",
|
||||
"354001": "Quake",
|
||||
"354002": "Stone Shield",
|
||||
"354003": "Cosmetic Fear",
|
||||
"354004": "Geospike",
|
||||
"354005": "Geospike",
|
||||
"354006": "Geospike",
|
||||
"354007": "Vek Death",
|
||||
"354008": "Vek Reset",
|
||||
"354009": "Quake",
|
||||
"354010": "Quake",
|
||||
"354011": "Stone Shield",
|
||||
"354012": "Stone Shield",
|
||||
"354013": "Resonance",
|
||||
"354014": "Resonance",
|
||||
"354015": "Geode Barrage",
|
||||
"354016": "Geode Barrage",
|
||||
"354017": "Ruptured Earth",
|
||||
"354018": "Ruptured Earth",
|
||||
"354019": "Stoneskin",
|
||||
"354020": "Stoneskin",
|
||||
"354021": "Stonebreaker",
|
||||
"354022": "Stonebreaker",
|
||||
"354023": "Stonebreaker",
|
||||
"354024": "Stonebreaker",
|
||||
"354025": "Geospike",
|
||||
"354026": "Geospike",
|
||||
"354027": "Geode Barrage",
|
||||
"354028": "Geode Barrage",
|
||||
"354029": "Geode Barrage",
|
||||
"354030": "Geode Barrage",
|
||||
"354031": "Resonite Cleanse",
|
||||
"354032": "Create Resonite Shard"
|
||||
},
|
||||
"families": {
|
||||
"control": [
|
||||
354007,
|
||||
354008
|
||||
],
|
||||
"quake": [
|
||||
354001,
|
||||
354009,
|
||||
354010
|
||||
],
|
||||
"stoneShield": [
|
||||
354002,
|
||||
354011,
|
||||
354012
|
||||
],
|
||||
"geospike": [
|
||||
354004,
|
||||
354005,
|
||||
354006,
|
||||
354025,
|
||||
354026
|
||||
],
|
||||
"resonance": [
|
||||
354013,
|
||||
354014
|
||||
],
|
||||
"geodeBarrage": [
|
||||
354015,
|
||||
354016,
|
||||
354027,
|
||||
354028,
|
||||
354029,
|
||||
354030
|
||||
],
|
||||
"rupturedEarth": [
|
||||
354017,
|
||||
354018
|
||||
],
|
||||
"stoneskin": [
|
||||
354019,
|
||||
354020
|
||||
],
|
||||
"stonebreaker": [
|
||||
354000,
|
||||
354021,
|
||||
354022,
|
||||
354023,
|
||||
354024
|
||||
],
|
||||
"resonite": [
|
||||
354031,
|
||||
354032
|
||||
]
|
||||
},
|
||||
"summonLinks": [
|
||||
{
|
||||
"spellId": 354004,
|
||||
"effectIndex": 0,
|
||||
"effectType": 28,
|
||||
"targetKind": "creature",
|
||||
"entryId": 254278
|
||||
},
|
||||
{
|
||||
"spellId": 354032,
|
||||
"effectIndex": 0,
|
||||
"effectType": 76,
|
||||
"targetKind": "gameobject",
|
||||
"entryId": 254051
|
||||
}
|
||||
]
|
||||
},
|
||||
"adaptation": {
|
||||
"status": "source-backed-custom-cadence-required",
|
||||
"populationPlan": {
|
||||
"disposition": "retain-current-route-as-explicit-adaptation",
|
||||
"packCount": 5,
|
||||
"membersPerPack": 3,
|
||||
"authoredMobs": [
|
||||
{
|
||||
"serverEntry": 4175632753,
|
||||
"name": "Forgotten Mine Stoneguard",
|
||||
"role": "shielding-melee"
|
||||
},
|
||||
{
|
||||
"serverEntry": 647988074,
|
||||
"name": "Forgotten Mine Geomancer",
|
||||
"role": "geode-ranged"
|
||||
},
|
||||
{
|
||||
"serverEntry": 2677742134,
|
||||
"name": "Forgotten Mine Quarry Brute",
|
||||
"role": "frontal-bruiser"
|
||||
}
|
||||
]
|
||||
},
|
||||
"encounterPlan": [
|
||||
"Geolord Vek 254272 remains at the provisional boss anchor and opens with Stone Shield.",
|
||||
"Stonebreaker is a telegraphed frontal strike; Quake is a short-range raid pulse; Geode Barrage repeatedly targets one player; Ruptured Earth creates a marked split-damage pillar.",
|
||||
"Geospike 354004 summons entry 254278 after a clearly visible ground telegraph. The hazard erupts with the matching Geospike damage row and then despawns.",
|
||||
"Three clickable Resonite Shard gameobjects 254051 appear at authored offsets around the boss. Clicking consumes one and applies Resonite Cleanse 354031, which removes one active earth protection or prevents the next Resonance hit.",
|
||||
"At 50 percent health, Resonance begins and Vek alternates Quake with Ruptured Earth. Missing crystals respawn once per 30 second cycle.",
|
||||
"The official changelog requires large, obvious clickable crystal bounds and a high-contrast Geospike telegraph."
|
||||
],
|
||||
"authoredGameObjectOffsetsFromBoss": [
|
||||
[
|
||||
-12,
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
6,
|
||||
0,
|
||||
10.392
|
||||
],
|
||||
[
|
||||
6,
|
||||
0,
|
||||
-10.392
|
||||
]
|
||||
],
|
||||
"authoredCadence": {
|
||||
"stonebreakerMs": 6500,
|
||||
"geodeBarrageMs": 11000,
|
||||
"geospikeMs": 15000,
|
||||
"quakeMs": 18000,
|
||||
"rupturedEarthMs": 22000,
|
||||
"resoniteCycleMs": 30000
|
||||
},
|
||||
"mustBeCustomAuthored": [
|
||||
"all trash identities, placements, and AI",
|
||||
"all boss cadence, target selection, and phase thresholds",
|
||||
"the exact Resonite Cleanse interaction because the server script is absent",
|
||||
"crystal placement, interaction bounds, and respawn rules",
|
||||
"Geospike hazard placement and lifetime",
|
||||
"boss, hazard, trash, and crystal visual composites",
|
||||
"loot and completion scripting"
|
||||
]
|
||||
},
|
||||
"unresolved": [
|
||||
"Paired low/high-value spell rows do not expose a safe difficulty-label mapping.",
|
||||
"The authoritative creature and gameobject spawn coordinates are absent.",
|
||||
"Creature template stats and visual display IDs are absent.",
|
||||
"The exact Resonite click result is only constrained by client spell text and official changelog behavior."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
# Ascension custom dungeon source recovery
|
||||
|
||||
This report separates recoverable client facts from the behavior that HealerMan must author for Tinkertech Showdown (821), Frozen Reach (908), and Forgotten Mine (1781). The machine-readable companion is `ASCENSION_CUSTOM_DUNGEON_RECOVERY.json`; its validator checks both the current generated catalogs and the original installed-client `Spell.dbc`.
|
||||
|
||||
## Result
|
||||
|
||||
| Map | Recovered exactly | Still requires adaptation |
|
||||
| --- | --- | --- |
|
||||
| 821 Tinkertech Showdown | Client environment, encounter 9013, boss creature 663012 | Every spawn, trash identity, gameobject, spell binding, boss schedule, phase, visual, loot, and completion rule |
|
||||
| 908 Frozen Reach | Client environment, encounters 2168/2170, Alva 97700, Arktos 97701, three summoned entries, and 85 encounter spell rows | Positions, stats, cast schedule, target rules, phase scripting, web hazards, visuals, loot, and completion rules |
|
||||
| 1781 Forgotten Mine | Client environment, encounters 5069/5071, Geolord Vek 254272, Geospike summon 254278, Resonite Shard gameobject 254051, and 33 encounter spell rows | Positions, stats, cast schedule, trash roster, crystal behavior, hazard lifetime, visuals, loot, and completion rules |
|
||||
|
||||
The local server/reference repositories, installed WDB caches, addon databases, and the public Questie Ascension snapshot do not contain surviving creature templates, world spawn rows, SmartAI, or encounter scripts for these maps. Client spell rows prove names and summon targets, but they do not prove cast cadence, ownership in every case, or coordinates.
|
||||
|
||||
## 821: Tinkertech Showdown
|
||||
|
||||
Exact client facts:
|
||||
|
||||
- Map directory: `TinketechShowdown` (the client spelling omits the second `r`). Source hash: `323c47c081d0a6c39daf9878284863c21e3e38dbced2ba3f58d1d157d080d41d`.
|
||||
- The recovered root WMO is `World/wmo/Dungeon/AZ_Subway/Subway_Dungeon.wmo`.
|
||||
- Manastorm encounter 9013 names Mechatron and binds it to creature 663012 in mode 2.
|
||||
- The player, trash, and boss anchors in the manifest are geometry-derived and provisional, not recovered server spawn points.
|
||||
|
||||
Candidate-only evidence:
|
||||
|
||||
- Spell 257438 `Rocket`.
|
||||
- Spells 257459-257461 `Gatling Missile Barrage` / `Gatling Missile`.
|
||||
- Spell 257462 `Sound: Goblin Head Machine Loop`.
|
||||
- Spell 257470 `Incineration Beam`, spell 257471 `Energy Ball`, and spells 257472-257473 `Electrified Zone` / `Electric Shock`.
|
||||
|
||||
Those rows form a coherent goblin-machine cluster, but no surviving source binds them to creature 663012. They must stay labeled as an adaptation if used.
|
||||
|
||||
Concrete adaptation:
|
||||
|
||||
- Retain the current route as five authored three-creature packs. Recast generated entries 2629079128, 3849447491, and 1787979165 as `Tinkertech Guardbot`, `Tinkertech Coilcaster`, and `Tinkertech Demolisher`; these IDs and names remain HealerMan-owned.
|
||||
- Keep Mechatron 663012 at the provisional boss anchor.
|
||||
- Use an authored rotation of Rocket reticles, Gatling lane impacts, Incineration Beam lane sweeps, and alternating Electrified Zones. At 35% health, accelerate the rotation and add two Coilcasters.
|
||||
- Model platform destruction as temporary lane disabling because the imported web geometry is immutable.
|
||||
|
||||
Everything in that combat implementation except the boss identity and candidate spell names is custom-authored. It should not be represented as recovered Ascension parity.
|
||||
|
||||
## 908: Frozen Reach
|
||||
|
||||
Exact client facts:
|
||||
|
||||
- Map directory: `Alvaencounter`. Source hash: `5ab771df79c0e062cea3ebda4e416a9245bcf8a4cc6da048077df5e674081f97`.
|
||||
- Manastorm encounters 2168 and 2170 name Alva and bind her to creature 97700 in modes 0 and 2.
|
||||
- The official Ascension database identifies Alva as creature 97700 and Arktos as creature 97701.
|
||||
- Spell 255363 summons creature 97703, spell 255421 summons creature 97731, and spell 255433 summons creature 97770. In context these are the Blast Arrow unit, Explosive Trap, and Alvajar add.
|
||||
- The client cluster spans 85 exact rows from 255347 through 255472. The principal families are Rain of Arrows, Unbridled Arrows, Blast Arrow, Cluster Shot, Pact of the Hunt, Ice Rock, Carnivorous Bite, Frost Shield, Set Ablaze, Blaze, Frost Strike, Frost Roar, Lacerate, Shield Wall, Shadow Shroud Surge/Rebirth, Vengeful Bolt, Shadow Veil Lance, Sphere of Grief and Wrath, Alva's Grief/Wrath, Explosive Trap, and Army of the Dead.
|
||||
- The official changelog confirms that the Blast Arrow/“Blast Shot” interaction can hit Arktos and that Shadow Shroud Rebirth reaches full damage scaling at five or more players.
|
||||
|
||||
Concrete adaptation:
|
||||
|
||||
- Remove the current 15 procedural hallway creatures (entries 1606324391, 854807736, and 2855026892). Available evidence describes a contained Alva/Arktos encounter and provides no support for that generic trash population.
|
||||
- Start Alva 97700 and Arktos 97701 together with Pact of the Hunt. Give Alva the recovered ranged/trap kit and Arktos the recovered bite, ice, roar, lacerate, and defensive kit.
|
||||
- Allow Blast Arrow to apply Set Ablaze to Arktos, forcing panic movement and increased damage taken.
|
||||
- On Arktos's death, leave Alva's Grief at the corpse, cast Shadow Shroud Rebirth, summon four Alvajar 97770, and move Alva into the recovered vengeance spell family.
|
||||
- Preserve D1/D2 spell variants as source data but use D0 for the current normal experience until a surviving difficulty mapping is found.
|
||||
|
||||
The exact cadence in the JSON is an explicit HealerMan proposal. Initial positions, transition rules, summon formation, stats, targeting, and hazard implementation all remain custom-authored.
|
||||
|
||||
## 1781: Forgotten Mine
|
||||
|
||||
Exact client facts:
|
||||
|
||||
- Map directory: `ForgottenMine`. Source hash: `693b7067aa1109e14beb944bc95aed8299bf64e2ec6ae3dc43f4fab7d18f57b6`.
|
||||
- Manastorm encounters 5069 and 5071 name Geolord Vek and bind him to creature 254272 in modes 0 and 2.
|
||||
- The contiguous 354000-354032 spell cluster explicitly contains `Vek Death` and `Vek Reset`, tying the cluster to Vek.
|
||||
- The recovered families are Stonebreaker, Quake, Stone Shield, Geospike, Resonance, Geode Barrage, Ruptured Earth, Stoneskin, Resonite Cleanse, and Create Resonite Shard.
|
||||
- Spell 354004 summons Geospike creature 254278. Spell 354032 creates gameobject 254051, named by the spell as a Resonite Shard.
|
||||
- Client text says Resonite counters the power of Earth. Official changelogs additionally require larger clickable crystal bounds and a visible Geospike telegraph.
|
||||
|
||||
Concrete adaptation:
|
||||
|
||||
- Retain five authored three-creature route packs only as an explicit approximation. Recast generated entries 4175632753, 647988074, and 2677742134 as `Forgotten Mine Stoneguard`, `Forgotten Mine Geomancer`, and `Forgotten Mine Quarry Brute`; these are not recovered Ascension creatures.
|
||||
- Keep Geolord Vek 254272 at the provisional boss anchor. Rotate Stonebreaker, Quake, Geode Barrage, Geospike, and Ruptured Earth, then begin Resonance at 50% health.
|
||||
- Spawn three clickable Resonite Shards 254051 at the authored triangular offsets in the JSON. Consuming a shard applies Resonite Cleanse 354031 and removes one active earth protection or prevents the next Resonance hit.
|
||||
- Give Geospike 254278 a high-contrast ground warning, eruption, and despawn sequence. Make crystal interaction bounds deliberately generous.
|
||||
|
||||
The crystal effect is constrained by spell text and changelog behavior but its exact server-side rule did not survive. Trash, timing, targeting, positions, respawn cadence, and visuals are custom-authored.
|
||||
|
||||
## Integration boundary
|
||||
|
||||
The recovery files intentionally do not change `compile-campaign-catalog.mjs`. Runtime integration should consume the recovered IDs and family lists while preserving three provenance labels:
|
||||
|
||||
- `exact`: copied from checksummed client/catalog data.
|
||||
- `spellLinked`: an entry ID read directly from a DBC summon effect; its descriptive name can still be contextual.
|
||||
- `authored`: HealerMan timing, placement, visuals, or behavior.
|
||||
|
||||
Validate the recovery against the current catalogs and installed client with:
|
||||
|
||||
```powershell
|
||||
node scripts/dungeon-pipeline/validate-ascension-custom-dungeon-recovery.mjs
|
||||
```
|
||||
|
||||
Use `--skip-dbc` only on machines without the checksummed Area-52 client, or pass another byte-identical file with `--dbc <path>`.
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/TempestKeepArcane",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_TempestKeep/TK_Arcane.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/AuchindounDraenei",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_Auchindoun/Draenei_wing.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "burning-crusade",
|
||||
"extraction": {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/Blackfathom",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [20, 30]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/BlackrockDepths",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [50, 60]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/BlackRockSpire",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [55, 60]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "blades-edge-arena",
|
||||
"title": "Blade's Edge Arena",
|
||||
"mapId": 562,
|
||||
"lfgDungeonIds": [],
|
||||
"expansion": "the-burning-crusade",
|
||||
"clientBuild": "3.3.5a",
|
||||
"mapDirectory": "World/Maps/bladesedgearena",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "rated", "difficulty": "normal", "spawnMask": 1, "levelRange": [80, 80]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/PvP/Buildings/AncientOrcArena/ancorc_PVPstadium.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
"navigation": {"cellSize": 0.2, "cellHeight": 0.1, "agentHeight": 1.8, "agentRadius": 0.36, "agentMaxClimb": 0.5, "agentMaxSlope": 50},
|
||||
"review": {"entrance": "required", "bossOrder": "not-applicable", "optionalEncounters": "not-applicable", "presentation": "required", "offMeshLinks": "required"},
|
||||
"exceptions": []
|
||||
}
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/HellfireDemon",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [58, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [58, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/Hellfire/hellfire_DemonWing.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/TempestKeepAtrium",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_TempestKeep/TK_Atrium.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "dalaran-sewers",
|
||||
"title": "Dalaran Sewers",
|
||||
"mapId": 617,
|
||||
"lfgDungeonIds": [],
|
||||
"expansion": "wrath-of-the-lich-king",
|
||||
"clientBuild": "3.3.5a",
|
||||
"mapDirectory": "World/Maps/DalaranArena",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "rated", "difficulty": "normal", "spawnMask": 1, "levelRange": [80, 80]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/PvP/Buildings/Dalaran/dalaran_sewer_arena.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
"navigation": {"cellSize": 0.2, "cellHeight": 0.1, "agentHeight": 1.8, "agentRadius": 0.36, "agentMaxClimb": 0.5, "agentMaxSlope": 50},
|
||||
"review": {"entrance": "required", "bossOrder": "not-applicable", "optionalEncounters": "not-applicable", "presentation": "required", "offMeshLinks": "required"},
|
||||
"exceptions": []
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/DireMaul",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source-transform",
|
||||
"coordinateTransform": {"origin": [0, 0, 0], "yawDegrees": 180},
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [50, 60]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "burning-crusade",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/GnomeragonInstance",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [25, 40]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -8,7 +8,12 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/HellfireRampart",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [58, 70]}],
|
||||
"coordinateAlignment": "source-transform",
|
||||
"coordinateTransform": {"origin": [1262.4427, -60.8902, 1622.5397], "yawDegrees": 262.143},
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [58, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/Hellfire/Hellfire_ramparts.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/Sunwell5ManFix",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [70, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [70, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/QT_Sunwell_5Man/Sunwell_5Man.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/AuchindounEthereal",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_Auchindoun/ethereal_wing.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/Mauradon",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [40, 55]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/TempestKeepFactory",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_TempestKeep/TK_Factory.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "nagrand-arena",
|
||||
"title": "Nagrand Arena",
|
||||
"mapId": 559,
|
||||
"lfgDungeonIds": [],
|
||||
"expansion": "the-burning-crusade",
|
||||
"clientBuild": "3.3.5a",
|
||||
"mapDirectory": "World/Maps/PVPZone01",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "rated", "difficulty": "normal", "spawnMask": 1, "levelRange": [80, 80]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_OgreHuts/PVP_Ogre_Arena01.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
"navigation": {"cellSize": 0.2, "cellHeight": 0.1, "agentHeight": 1.8, "agentRadius": 0.36, "agentMaxClimb": 0.5, "agentMaxSlope": 50},
|
||||
"review": {"entrance": "required", "bossOrder": "not-applicable", "optionalEncounters": "not-applicable", "presentation": "required", "offMeshLinks": "required"},
|
||||
"exceptions": []
|
||||
}
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/OrgrimmarInstance",
|
||||
"environmentMode": "global-wmo",
|
||||
"globalWmoAlignment": "source",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [13, 25]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/RazorfenDowns",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source-transform",
|
||||
"coordinateTransform": {"origin": [-2402.2809, 62, 1208.3219], "yawDegrees": 0},
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [35, 50]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/RazorfenKraulInstance",
|
||||
"environmentMode": "global-wmo",
|
||||
"globalWmoSourceBasis": "flip-x",
|
||||
"coordinateAlignment": "source-transform",
|
||||
"coordinateTransform": {"origin": [-1952.1803, 87.684, 1587.8923], "yawDegrees": -133},
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [25, 40]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "ring-of-valor",
|
||||
"title": "Ring of Valor",
|
||||
"mapId": 618,
|
||||
"lfgDungeonIds": [],
|
||||
"expansion": "wrath-of-the-lich-king",
|
||||
"clientBuild": "3.3.5a",
|
||||
"mapDirectory": "World/Maps/OrgrimmarArena",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "rated", "difficulty": "normal", "spawnMask": 1, "levelRange": [80, 80]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/PvP/Buildings/Orgrimmar/OrgrimmarArena.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
"navigation": {"cellSize": 0.2, "cellHeight": 0.1, "agentHeight": 1.8, "agentRadius": 0.36, "agentMaxClimb": 0.5, "agentMaxSlope": 50},
|
||||
"review": {"entrance": "required", "bossOrder": "not-applicable", "optionalEncounters": "not-applicable", "presentation": "required", "offMeshLinks": "required"},
|
||||
"exceptions": []
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"slug": "ruins-of-lordaeron",
|
||||
"title": "Ruins of Lordaeron",
|
||||
"mapId": 572,
|
||||
"lfgDungeonIds": [],
|
||||
"expansion": "the-burning-crusade",
|
||||
"clientBuild": "3.3.5a",
|
||||
"mapDirectory": "World/Maps/PVPLordaeron",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "rated", "difficulty": "normal", "spawnMask": 1, "levelRange": [80, 80]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/PvP/Buildings/Lordaeron/PVP_Lordaeron_Arena.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
"navigation": {"cellSize": 0.2, "cellHeight": 0.1, "agentHeight": 1.8, "agentRadius": 0.36, "agentMaxClimb": 0.5, "agentMaxSlope": 50},
|
||||
"review": {"entrance": "required", "bossOrder": "not-applicable", "optionalEncounters": "not-applicable", "presentation": "required", "offMeshLinks": "required"},
|
||||
"exceptions": []
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
"clientBuild": "3.3.5a",
|
||||
"mapDirectory": "World/Maps/MonasteryInstances",
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"adtSourceBasis": "z-y-negative-x",
|
||||
"difficultyVariants": [
|
||||
{ "id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [26, 45] }
|
||||
],
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/SchoolofNecromancy",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source-transform",
|
||||
"coordinateTransform": {"origin": [-141.694, 148.0683, 105.6784], "yawDegrees": 1},
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [55, 60]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/AuchindounDemon",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_Auchindoun/demon_wing.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/AuchindounShadow",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [65, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_Auchindoun/shadow_council_wing.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
"expansion": "classic",
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/Shadowfang",
|
||||
"environmentMode": "global-wmo",
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"adtSourceBasis": "z-y-negative-x",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [18, 30]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/LD_ShadowFang/LD_ShadowFang.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
"navigation": {"cellSize": 0.2, "cellHeight": 0.1, "agentHeight": 1.8, "agentRadius": 0.36, "agentMaxClimb": 0.5, "agentMaxSlope": 50},
|
||||
"extraction": {"adtTiles": "all-present"},
|
||||
"navigation": {"cellSize": 0.2, "cellHeight": 0.1, "tileSize": 256, "retainTileIntermediates": false, "agentHeight": 1.8, "agentRadius": 0.36, "agentMaxClimb": 0.5, "agentMaxSlope": 50},
|
||||
"review": {"entrance": "required", "bossOrder": "required", "optionalEncounters": "required", "presentation": "required", "offMeshLinks": "required"},
|
||||
"exceptions": []
|
||||
}
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/HellfireMilitary",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [60, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [60, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/Hellfire/Hellfire_Military.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/CoilfangDraenei",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [60, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [60, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_Coilfang/Coilfang_Draenei.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/CoilfangPumping",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [60, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [60, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_Coilfang/Coilfang_Pumping.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
"agentMaxSlope": 50
|
||||
},
|
||||
"review": {
|
||||
"entrance": "required",
|
||||
"entrance": "approved",
|
||||
"bossOrder": "required",
|
||||
"optionalEncounters": "required",
|
||||
"presentation": "required",
|
||||
"offMeshLinks": "required"
|
||||
"presentation": "approved",
|
||||
"offMeshLinks": "approved"
|
||||
},
|
||||
"exceptions": []
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/Stratholme",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source-transform",
|
||||
"coordinateTransform": {"origin": [-3449.6608, 144.3848, -3379.6966], "yawDegrees": 0},
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [55, 60]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/SunkenTemple",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [45, 55]}],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/Uldaman",
|
||||
"environmentMode": "global-wmo",
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{
|
||||
"id": "normal",
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/CoilfangMarsh",
|
||||
"environmentMode": "global-wmo",
|
||||
"difficultyVariants": [{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [60, 70]}],
|
||||
"coordinateAlignment": "source",
|
||||
"difficultyVariants": [
|
||||
{"id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [60, 70]},
|
||||
{"id": "heroic", "difficulty": "heroic", "spawnMask": 2, "levelRange": [70, 70]}
|
||||
],
|
||||
"clientSource": {"rootEnvironmentVariable": "WOW_CLIENT_ROOT", "relativeDefault": "../wow335a"},
|
||||
"serverSource": {"rootEnvironmentVariable": "WOW_SERVER_DATA", "adapter": "normalized-json", "readOnly": true},
|
||||
"extraction": {"rootWmo": "World/wmo/Dungeon/OL_Coilfang/Coilfang_Marsh.wmo", "doodadSets": ["Set_$DefaultGlobal"]},
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
}
|
||||
],
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"exceptions": [],
|
||||
"expansion": "wrath",
|
||||
"extraction": {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
"clientBuild": "3.3.5a-Ascension",
|
||||
"mapDirectory": "World/Maps/TanarisInstance",
|
||||
"environmentMode": "detect",
|
||||
"coordinateAlignment": "source",
|
||||
"adtSourceBasis": "z-y-negative-x",
|
||||
"difficultyVariants": [
|
||||
{ "id": "normal", "difficulty": "normal", "spawnMask": 1, "levelRange": [40, 54] }
|
||||
],
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"dungeonId": "stormwind-stockade",
|
||||
"navigationLinks": [
|
||||
{
|
||||
"id": "entrance-to-central-stairs",
|
||||
"start": [-66.674328, -23.150503, 5.501851],
|
||||
"end": [-67.664446, -23.755576, 4.493397],
|
||||
"bidirectional": true,
|
||||
"authoredReason": "Bridges the reviewed 1.537m Recast seam between the source entrance stairs and the central cell block."
|
||||
},
|
||||
{
|
||||
"id": "central-to-north-wing-stairs",
|
||||
"start": [-124.46789, -27.349338, 16.099782],
|
||||
"end": [-125.476344, -27.95441, 17.0899],
|
||||
"bidirectional": true,
|
||||
"authoredReason": "Bridges the reviewed 1.537m Recast seam from the central cell block to the north wing stairs."
|
||||
},
|
||||
{
|
||||
"id": "central-to-south-wing-stairs",
|
||||
"start": [-124.46789, -27.349338, -14.703893],
|
||||
"end": [-125.476344, -27.95441, -15.694011],
|
||||
"bidirectional": true,
|
||||
"authoredReason": "Bridges the reviewed 1.537m Recast seam from the central cell block to the south wing stairs."
|
||||
}
|
||||
],
|
||||
"traversalRamps": [
|
||||
{
|
||||
"id": "entrance-stair-ramp",
|
||||
"start": [-52.8, -18.16, 0.8],
|
||||
"end": [-72.4, -25.609, 0.8],
|
||||
"width": 10.8,
|
||||
"navigationBridge": {
|
||||
"linkId": "entrance-to-central-stairs",
|
||||
"startPortal": [[-66.674328, -23.150503, -3.904271], [-66.674328, -23.150503, 5.501851]],
|
||||
"endPortal": [[-67.664446, -23.755576, -2.895818], [-67.664446, -23.755576, 4.493397]]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "north-wing-stair-ramp",
|
||||
"start": [-129.17, -25.609, 11.7],
|
||||
"end": [-129.17, -33.944, 32.85],
|
||||
"width": 10.8,
|
||||
"navigationBridge": {
|
||||
"linkId": "central-to-north-wing-stairs",
|
||||
"startPortal": [[-133.878601, -27.349338, 16.099782], [-124.46789, -27.349338, 16.099782]],
|
||||
"endPortal": [[-132.668457, -27.95441, 17.0899], [-125.476344, -27.95441, 17.0899]]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "south-wing-stair-ramp",
|
||||
"start": [-129.17, -25.609, -10.3],
|
||||
"end": [-129.17, -33.944, -31.45],
|
||||
"width": 10.8,
|
||||
"navigationBridge": {
|
||||
"linkId": "central-to-south-wing-stairs",
|
||||
"startPortal": [[-133.878601, -27.349338, -14.703893], [-124.46789, -27.349338, -14.703893]],
|
||||
"endPortal": [[-132.668457, -27.95441, -15.694011], [-125.476344, -27.95441, -15.694011]]
|
||||
}
|
||||
}
|
||||
],
|
||||
"presentation": {
|
||||
"background": "#171815",
|
||||
"fog": {
|
||||
"color": "#1b1c18",
|
||||
"near": 44
|
||||
},
|
||||
"ambientLight": {
|
||||
"color": "#d8c9aa",
|
||||
"intensity": 0.58
|
||||
},
|
||||
"hemisphereLight": {
|
||||
"skyColor": "#cfc3a5",
|
||||
"groundColor": "#292820",
|
||||
"intensity": 0.86
|
||||
},
|
||||
"directionalLight": {
|
||||
"color": "#ffe0a8",
|
||||
"intensity": 1.1,
|
||||
"offset": [10, 18, -5]
|
||||
}
|
||||
},
|
||||
"validation": {
|
||||
"navigation": "green",
|
||||
"report": "PLAYTEST_NOTES.md",
|
||||
"warnings": [
|
||||
"Three reviewed smooth stair collision proxies and filled navigation bridges connect the entrance, central block, and both prison wings; minor decorative navmesh islands remain intentionally discarded.",
|
||||
"Stockades uses a dungeon-specific warm indoor lighting profile because the imported WMO has no baked lightmap or vertex-lighting channel."
|
||||
]
|
||||
}
|
||||
}
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "healer-man",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.41",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "healer-man",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.41",
|
||||
"dependencies": {
|
||||
"@capacitor/android": "8.4.1",
|
||||
"@capacitor/core": "8.4.1",
|
||||
|
||||
+37
-8
@@ -1,26 +1,31 @@
|
||||
{
|
||||
"name": "healer-man",
|
||||
"private": true,
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.41",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
"dev:shelved": "vite --host 0.0.0.0 --mode shelved",
|
||||
"start": "node server/server.mjs",
|
||||
"db:init": "node scripts/init-game-database.mjs",
|
||||
"db:backup": "node scripts/backup-game-database.mjs",
|
||||
"server:test": "node --test server/*.test.mjs",
|
||||
"content:publish": "node scripts/content/publish-content.mjs",
|
||||
"web:prepare": "node scripts/content/prepare-web-bootstrap.mjs",
|
||||
"android:prepare": "node scripts/content/prepare-android-bootstrap.mjs",
|
||||
"content:test": "node --test scripts/content/*.test.mjs",
|
||||
"effects:generate": "node scripts/combat-effects/generate-manifest.mjs --package-audio",
|
||||
"effects:audit": "node scripts/combat-effects/generate-manifest.mjs --package-audio --audit",
|
||||
"effects:generate": "node scripts/combat-effects/generate-manifest.mjs --package-audio && node scripts/combat-effects/package-wow-original.mjs",
|
||||
"effects:audit": "node scripts/combat-effects/package-wow-original.mjs --audit",
|
||||
"effects:wow": "node scripts/combat-effects/package-wow-original.mjs",
|
||||
"effects:priest": "npm run effects:wow",
|
||||
"effects:test": "node --test scripts/combat-effects/*.test.mjs",
|
||||
"wow335:abilities": "node scripts/generate-wow335-abilities.mjs",
|
||||
"coa:sync": "node scripts/sync-ascension-coa.mjs",
|
||||
"coa:sync:live": "node scripts/sync-ascension-coa.mjs --tooltips",
|
||||
"coa:icons": "node scripts/export-spell-icons.mjs",
|
||||
"ascension:talents": "node scripts/generate-wow335-talents.mjs ../wow335a/Interface/AddOns/PlayerBotManager/PBM/PBM_TalentData.lua ../LadiksMPQEditor/client-current/area-52/patch-D/DBFilesClient/Spell.dbc src/game/wow335Talents.generated.ts ../LadiksMPQEditor/client-current/area-52/patch-D/DBFilesClient/Talent.dbc",
|
||||
"ascension:sync": "npm run coa:sync && npm run wow335:abilities && npm run ascension:talents && npm run coa:icons",
|
||||
"catalogs:shard": "vite-node scripts/generate-catalog-shards.ts",
|
||||
"ascension:sync": "npm run coa:sync && npm run wow335:abilities && npm run ascension:talents && npm run coa:icons && npm run catalogs:shard",
|
||||
"manastorm:import": "node scripts/manastorm-import/cli.mjs",
|
||||
"manastorm:forensics": "node scripts/manastorm-import/forensics-cli.mjs",
|
||||
"manastorm:sync": "npm run manastorm:import && npm run manastorm:forensics && npm run coa:icons",
|
||||
@@ -40,6 +45,7 @@
|
||||
"dungeon:epoch:sync": "node scripts/dungeon-pipeline/sync-epoch-dungeons.mjs",
|
||||
"dungeon:source:azerothcore": "node scripts/dungeon-pipeline/import-azerothcore-world.mjs",
|
||||
"dungeon:catalog": "node scripts/dungeon-pipeline/compile-campaign-catalog.mjs",
|
||||
"dungeon:maps:import": "node scripts/dungeon-pipeline/import-wow-maps.mjs",
|
||||
"dungeon:canonicalize-environments": "node scripts/dungeon-pipeline/relink-canonical-environments.mjs",
|
||||
"dungeon:dedupe-creatures": "node scripts/dungeon-pipeline/dedupe-shared-creatures.mjs",
|
||||
"dungeon:test-creature-animation-policy": "node --test scripts/dungeon-pipeline/runtime-creature-animations.test.mjs",
|
||||
@@ -49,8 +55,13 @@
|
||||
"dungeon:five-player:import": "node scripts/dungeon-pipeline/import-five-player-dungeon.mjs",
|
||||
"dungeon:five-player:import:remaining": "node scripts/dungeon-pipeline/import-remaining-five-player-dungeons.mjs",
|
||||
"dungeon:five-player:verify": "node scripts/dungeon-pipeline/verify-five-player-dungeon-imports.mjs",
|
||||
"dungeon:wow:audit": "node scripts/dungeon-pipeline/audit-wow-dungeon-content.mjs",
|
||||
"dungeon:wow:audit": "vite-node scripts/dungeon-pipeline/audit-wow-dungeon-content.mjs",
|
||||
"dungeon:wow:functionality:audit": "vite-node scripts/dungeon-pipeline/audit-wow-dungeon-functionality.ts",
|
||||
"dungeon:wow:stairs:audit": "node --max-old-space-size=8192 scripts/dungeon-pipeline/audit-wow-stairs.ts",
|
||||
"dungeon:wow:stairs:refresh": "node --max-old-space-size=8192 scripts/dungeon-pipeline/audit-wow-stairs.ts --write",
|
||||
"dungeon:wow:test": "vitest run src/game/wowDungeonContent.test.ts",
|
||||
"dungeon:wow:export-tool:test": "node --test scripts/dungeon-pipeline/wow-export-tool.test.mjs",
|
||||
"dungeon:ascension:recovery:validate": "node scripts/dungeon-pipeline/validate-ascension-custom-dungeon-recovery.mjs --skip-dbc",
|
||||
"dungeon:wow:all": "node scripts/dungeon-pipeline/run-all-wow-dungeons.mjs",
|
||||
"dungeon:extract": "node scripts/dungeon-pipeline/cli.mjs extract",
|
||||
"dungeon:creatures": "node scripts/dungeon-pipeline/cli.mjs creatures",
|
||||
@@ -60,6 +71,9 @@
|
||||
"dungeon:navmesh": "node scripts/dungeon-pipeline/cli.mjs navmesh",
|
||||
"dungeon:navmesh:conversion": "node scripts/dungeon-pipeline/cli.mjs navmesh-conversion",
|
||||
"dungeon:validate": "node scripts/dungeon-pipeline/cli.mjs validate",
|
||||
"arena:assets:build": "node scripts/arena-pipeline/build-wmo-arenas.mjs && node scripts/arena-pipeline/build-custom-arenas.mjs && node scripts/arena-pipeline/prepare-warnorken.mjs",
|
||||
"arena:assets:validate": "node scripts/arena-pipeline/validate-arenas.mjs",
|
||||
"arena:abilities:build": "node scripts/arena-pipeline/build-combat-runtime.mjs && node scripts/arena-pipeline/build-runtime-geometry.mjs",
|
||||
"dungeon:enable": "node scripts/dungeon-pipeline/cli.mjs enable",
|
||||
"runewaker:forsaken": "node \"%INIT_CWD%\\scripts\\runewaker-pipeline\\build-forsaken-abbey.mjs\"",
|
||||
"runewaker:forsaken:actors": "node scripts/runewaker-pipeline/build-forsaken-population.mjs --actors-only",
|
||||
@@ -71,6 +85,9 @@
|
||||
"runewaker:register:all": "node scripts/runewaker-pipeline/cli.mjs register-all",
|
||||
"runewaker:refresh:all": "node scripts/runewaker-pipeline/cli.mjs refresh-all",
|
||||
"runewaker:spells:refresh": "node scripts/runewaker-pipeline/export-spell-catalog.mjs",
|
||||
"runewaker:loot:refresh": "node scripts/runewaker-pipeline/export-loot-catalog.mjs",
|
||||
"runewaker:audit:formula-loot": "node --test scripts/runewaker-pipeline/runewaker-formula-export.test.mjs scripts/runewaker-pipeline/runewaker-loot-import.test.mjs && vitest run src/game/runewakerCombatFormula.test.ts src/game/runewakerLootCatalog.test.ts",
|
||||
"runewaker:bosses:refresh": "npm run runewaker:spells:refresh && node scripts/runewaker-pipeline/import-boss-mechanics.mjs",
|
||||
"runewaker:players:refresh": "node scripts/runewaker-pipeline/export-player-catalog.mjs && npm run runewaker:players:avatars",
|
||||
"runewaker:players:avatars": "node scripts/runewaker-pipeline/build-population.mjs --recipe scripts/runewaker-pipeline/recipes/runewaker-player-avatars.json --actors-only && node scripts/runewaker-pipeline/finalize-player-avatar-manifest.mjs",
|
||||
"runewaker:paperdolls:refresh": "node scripts/runewaker-pipeline/refresh-paperdoll-appearances.mjs",
|
||||
@@ -80,8 +97,11 @@
|
||||
"runewaker:audit:pose-batch": "node scripts/runewaker-pipeline/audit-animated-poses.mjs",
|
||||
"runewaker:audit:pose-master": "node scripts/runewaker-pipeline/generate-master-actor-pose-audit.mjs",
|
||||
"runewaker:audit:animations": "npm run runewaker:audit:animation-readiness && vitest run src/game/runewakerDungeonAnimation.test.ts src/game/mobAnimation.test.ts src/game/runewakerDungeonRegistry.test.ts",
|
||||
"runewaker:audit:gameplay": "node scripts/runewaker-pipeline/audit-gameplay-content.mjs",
|
||||
"runewaker:audit:gameplay": "node scripts/runewaker-pipeline/audit-gameplay-content.mjs && node --test scripts/runewaker-pipeline/encounter-implementation-report.test.mjs",
|
||||
"runewaker:audit:encounters": "node scripts/runewaker-pipeline/audit-encounter-implementation.mjs && node --test scripts/runewaker-pipeline/encounter-implementation-report.test.mjs",
|
||||
"runewaker:audit:environment-assembly": "node scripts/runewaker-pipeline/audit-environment-assembly.mjs && node --test scripts/runewaker-pipeline/environment-assembly.test.mjs",
|
||||
"runewaker:audit:navigation": "node scripts/runewaker-pipeline/analyze-navigation-gaps.mjs && vitest run src/game/runewakerObjectiveRouteAudit.test.ts",
|
||||
"runewaker:audit:material-alpha": "node scripts/runewaker-pipeline/audit-material-alpha.mjs",
|
||||
"assets:wailing:instance": "gltf-transform instance ../wow335a/dungeon-export-work/WailingCaverns/converted/wailing-caverns.visual.glb src/assets/game/dungeons/wailing-caverns/wailing-caverns-visual.glb --min 3",
|
||||
"assets:wailing:creatures:animated": "node scripts/prepareAnimatedCreatures.mjs",
|
||||
"assets:wailing:creatures:combat": "node scripts/add-combat-creature-clips.mjs",
|
||||
@@ -91,17 +111,26 @@
|
||||
"assets:ktx2:creatures": "node scripts/asset-pipeline/compress-creature-ktx2.mjs",
|
||||
"assets:ktx2:audit": "node scripts/asset-pipeline/audit-ktx2.mjs --visual-only --require-all",
|
||||
"assets:ktx2:refresh-metadata": "node scripts/asset-pipeline/refresh-ktx2-metadata.mjs",
|
||||
"prebuild": "npm run loot:generate && npm run assets:ktx2:audit",
|
||||
"assets:performance:audit": "node scripts/asset-pipeline/audit-environment-performance.mjs",
|
||||
"balance:benchmark": "vite-node scripts/combat-benchmark/run.ts",
|
||||
"balance:smoke": "vite-node scripts/combat-benchmark/run.ts --class mage --role damage --scenario damage-single-burst --seeds 1 --duration-scale 0.2",
|
||||
"prebuild": "npm run loot:generate && npm run web:prepare && npm run assets:ktx2:audit && npm run assets:performance:audit",
|
||||
"build": "node --max-old-space-size=8192 node_modules/typescript/bin/tsc -b && node --max-old-space-size=8192 node_modules/vite/bin/vite.js build",
|
||||
"build:android:web": "npm run prebuild && npm run android:prepare && node --max-old-space-size=8192 node_modules/typescript/bin/tsc -b && node --max-old-space-size=8192 node_modules/vite/bin/vite.js build --mode android",
|
||||
"postbuild": "node scripts/content/precompress-static.mjs dist && node scripts/content/verify-release-bundle.mjs dist",
|
||||
"prebuild:shelved": "npm run loot:generate && npm run arena:abilities:build && npm run web:prepare && npm run arena:assets:validate && npm run assets:ktx2:audit && npm run assets:performance:audit",
|
||||
"build:shelved": "node --max-old-space-size=8192 node_modules/typescript/bin/tsc -b && node --max-old-space-size=8192 node_modules/vite/bin/vite.js build --mode shelved",
|
||||
"build:android:web": "npm run prebuild && npm run android:prepare && node --max-old-space-size=8192 node_modules/typescript/bin/tsc -b && node --max-old-space-size=8192 node_modules/vite/bin/vite.js build --mode android && node scripts/content/verify-release-bundle.mjs dist-android",
|
||||
"android:sync": "npm run build:android:web && cap sync android",
|
||||
"android:apk": "npm run android:sync && powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/runAndroidGradle.ps1 assembleDebug",
|
||||
"android:apk:release": "npm run android:sync && powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/runAndroidGradle.ps1 assembleRelease",
|
||||
"android:install": "npm run android:sync && powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/runAndroidGradle.ps1 installDebug",
|
||||
"release:check": "python scripts/release_manager.py --check-release",
|
||||
"release:gui": "python scripts/release_manager.py",
|
||||
"preview": "vite preview --host 0.0.0.0",
|
||||
"pretest": "npm run loot:generate",
|
||||
"test": "vitest run",
|
||||
"pretest:shelved": "npm run loot:generate",
|
||||
"test:shelved": "vitest run --mode shelved",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -11,6 +11,157 @@
|
||||
},
|
||||
"proceduralFallbacks": [],
|
||||
"assets": [
|
||||
{
|
||||
"sourceModel": "model/character/creature/atmo/act_atmo.ros",
|
||||
"appearanceSignature": null,
|
||||
"sourceModelSha256": "937f0ae59fd87e011479d67deb7baf1023cdb0f38ef72d78d9e923b99c4088a9",
|
||||
"checksum": "9f7c1ad1c56ef51ee0511507f950ef97224ad86af561ded111cf1d5740c69999",
|
||||
"size": 1981260,
|
||||
"triangles": 3176,
|
||||
"bounds": {
|
||||
"min": [
|
||||
-0.41683,
|
||||
0.00233,
|
||||
-0.15372
|
||||
],
|
||||
"max": [
|
||||
0.41683,
|
||||
2.00238,
|
||||
1.86916
|
||||
]
|
||||
},
|
||||
"groundOffset": -0.00233,
|
||||
"labelHeight": 2.35005,
|
||||
"markerRadius": 0.64732,
|
||||
"animations": [
|
||||
"Action - buff_sp01",
|
||||
"Action - buff01",
|
||||
"Cast - cast_sp01",
|
||||
"Cast - cast01",
|
||||
"Cast - casting_delay",
|
||||
"Action - continuous_buff",
|
||||
"Terminal Pose - dead",
|
||||
"Death - death",
|
||||
"Action - dodge",
|
||||
"Wound - hurt",
|
||||
"Idle - idle02",
|
||||
"Idle - idle03",
|
||||
"Run - run_forward",
|
||||
"Stand - stand_idle01",
|
||||
"Attack - unarmed_attack01",
|
||||
"Attack - unarmed_attack02",
|
||||
"Idle - unarmed_idle",
|
||||
"Action - unarmed_parry",
|
||||
"Walk - walk_forward"
|
||||
],
|
||||
"id": "atmo",
|
||||
"baseActorId": "atmo",
|
||||
"fileName": "atmo.glb",
|
||||
"url": "/assets/creatures/grafu-castle/atmo.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/barbo_brother/act_barbo_brother.ros",
|
||||
"appearanceSignature": null,
|
||||
"sourceModelSha256": "8caafccd798e69e31f09a0d270c14e938c913935e9fd427691299b0a5d2db2a2",
|
||||
"checksum": "af1cd20b7cc1d465c270860997f1245b2967005b1e5308b4a43401e983f883a4",
|
||||
"size": 2226000,
|
||||
"triangles": 3371,
|
||||
"bounds": {
|
||||
"min": [
|
||||
-3.23727,
|
||||
-1.32298,
|
||||
-1.36027
|
||||
],
|
||||
"max": [
|
||||
1.54844,
|
||||
3.76779,
|
||||
1.15116
|
||||
]
|
||||
},
|
||||
"groundOffset": 1.32298,
|
||||
"labelHeight": 5.44077,
|
||||
"markerRadius": 1.53143,
|
||||
"animations": [
|
||||
"Action - knockdown_start",
|
||||
"Action - buff01",
|
||||
"Action - buff_sp01",
|
||||
"Cast - cast01",
|
||||
"Cast - cast_sp01",
|
||||
"Cast - casting_delay",
|
||||
"Action - continuous_buff",
|
||||
"Terminal Pose - dead",
|
||||
"Death - death",
|
||||
"Action - dodge",
|
||||
"Wound - hurt",
|
||||
"Idle - idle02",
|
||||
"Idle - idle03",
|
||||
"Action - knockdown_recover",
|
||||
"Walk - walk_forward",
|
||||
"Run - run_forward",
|
||||
"Stand - stand_idle01",
|
||||
"Attack - unarmed_attack01",
|
||||
"Attack - unarmed_attack02",
|
||||
"Idle - unarmed_idle",
|
||||
"Action - unarmed_parry"
|
||||
],
|
||||
"id": "barbo-brother",
|
||||
"baseActorId": "barbo-brother",
|
||||
"fileName": "barbo-brother.glb",
|
||||
"url": "/assets/creatures/grafu-castle/barbo-brother.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/black_hat/act_black_hat.ros",
|
||||
"appearanceSignature": null,
|
||||
"sourceModelSha256": "1e16384ee51d97cb691ce2bb9c53e7f7c13c067bc40c09f4bec5fd8dc6f8b8f4",
|
||||
"checksum": "e3d5265c794b82d45c34bd7875e33c9f00680dc67c3cc18213f5df657f9ab76a",
|
||||
"size": 1965712,
|
||||
"triangles": 2222,
|
||||
"bounds": {
|
||||
"min": [
|
||||
-0.96974,
|
||||
-0.01676,
|
||||
-0.63427
|
||||
],
|
||||
"max": [
|
||||
0.92815,
|
||||
3.14637,
|
||||
1.25604
|
||||
]
|
||||
},
|
||||
"groundOffset": 0.01676,
|
||||
"labelHeight": 3.51313,
|
||||
"markerRadius": 0.60732,
|
||||
"animations": [
|
||||
"Action - buff_sp01",
|
||||
"Action - buff01",
|
||||
"Cast - cast_sp01",
|
||||
"Cast - cast01",
|
||||
"Cast - casting_delay",
|
||||
"Action - continuous_buff",
|
||||
"Terminal Pose - dead",
|
||||
"Death - death",
|
||||
"Action - dodge",
|
||||
"Wound - hurt",
|
||||
"Idle - idle02",
|
||||
"Idle - idle03",
|
||||
"Action - knockdown_end",
|
||||
"Action - knockdown_recover",
|
||||
"Run - run_forward",
|
||||
"Stand - stand_idle01",
|
||||
"Attack - unarmed_attack01",
|
||||
"Attack - unarmed_attack02",
|
||||
"Idle - unarmed_idle",
|
||||
"Action - unarmed_parry",
|
||||
"Walk - walk_forward"
|
||||
],
|
||||
"id": "black-hat",
|
||||
"baseActorId": "black-hat",
|
||||
"fileName": "black-hat.glb",
|
||||
"url": "/assets/creatures/grafu-castle/black-hat.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/black_hat/act_black_hat_blue.ros",
|
||||
"appearanceSignature": null,
|
||||
@@ -206,6 +357,58 @@
|
||||
"url": "/assets/creatures/grafu-castle/controlled-steel-guardian.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/lost_protector/act_cursed_steel_guardian.ros",
|
||||
"appearanceSignature": null,
|
||||
"sourceModelSha256": "b31342cb3ee28f6d6b0b70af334ab835e5212722bc99d0c8818568d5b9e079df",
|
||||
"checksum": "3ec7660d4e1b475ec9ae290c304802dd4ba174048fac0781ae25474a718227fe",
|
||||
"size": 1151612,
|
||||
"triangles": 2201,
|
||||
"bounds": {
|
||||
"min": [
|
||||
-0.97829,
|
||||
-0.00095,
|
||||
-0.38972
|
||||
],
|
||||
"max": [
|
||||
0.97829,
|
||||
2.01359,
|
||||
0.26596
|
||||
]
|
||||
},
|
||||
"groundOffset": 0.00095,
|
||||
"labelHeight": 2.36454,
|
||||
"markerRadius": 0.62611,
|
||||
"animations": [
|
||||
"Attack - 1h_attack01",
|
||||
"Attack - 1h_attack02",
|
||||
"Idle - 1h_idle",
|
||||
"Action - 1h_parry",
|
||||
"Cast - casting01",
|
||||
"Cast - casting02",
|
||||
"Cast - casting_delay",
|
||||
"Terminal Pose - dead",
|
||||
"Death - death",
|
||||
"Action - dodge",
|
||||
"Wound - hurt",
|
||||
"Idle - idle02",
|
||||
"Idle - idle03",
|
||||
"Run - run_forward",
|
||||
"Stand - stand_idle01",
|
||||
"Walk - walk_forward",
|
||||
"Attack - unarmed_attack01",
|
||||
"Attack - unarmed_attack02",
|
||||
"Idle - unarmed_idle",
|
||||
"Action - unarmed_parry",
|
||||
"Action - knockdown_end",
|
||||
"Action - knockdown_recover"
|
||||
],
|
||||
"id": "cursed-steel-guardian",
|
||||
"baseActorId": "cursed-steel-guardian",
|
||||
"fileName": "cursed-steel-guardian.glb",
|
||||
"url": "/assets/creatures/grafu-castle/cursed-steel-guardian.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/elder_myconid/act_elder_myconid.ros",
|
||||
"appearanceSignature": null,
|
||||
@@ -924,6 +1127,56 @@
|
||||
"url": "/assets/creatures/grafu-castle/hm-male-image-585705.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/karth/act_karth.ros",
|
||||
"appearanceSignature": null,
|
||||
"sourceModelSha256": "18d43dd054c85512bfdbde50093c9dee6b368cef088d7c0f6cc3e50fff8e3ea3",
|
||||
"checksum": "ac96b24461b39cdfae7810097dbe6698f0d7ce1afe235df04d72144dceef893d",
|
||||
"size": 1993572,
|
||||
"triangles": 3052,
|
||||
"bounds": {
|
||||
"min": [
|
||||
-1.18195,
|
||||
-0.00007,
|
||||
-0.72488
|
||||
],
|
||||
"max": [
|
||||
1.18195,
|
||||
2.6109,
|
||||
0.72657
|
||||
]
|
||||
},
|
||||
"groundOffset": 0.00007,
|
||||
"labelHeight": 2.96097,
|
||||
"markerRadius": 0.75645,
|
||||
"animations": [
|
||||
"Walk - walk_forward",
|
||||
"Action - buff01",
|
||||
"Action - buff_sp01",
|
||||
"Cast - cast01",
|
||||
"Cast - cast_sp01",
|
||||
"Cast - casting_delay",
|
||||
"Terminal Pose - dead",
|
||||
"Death - death",
|
||||
"Action - dodge",
|
||||
"Wound - hurt",
|
||||
"Idle - idle02",
|
||||
"Idle - idle03",
|
||||
"Run - run_forward",
|
||||
"Stand - stand_idle01",
|
||||
"Attack - unarmed_attack01",
|
||||
"Attack - unarmed_attack02",
|
||||
"Idle - unarmed_idle",
|
||||
"Action - unarmed_parry",
|
||||
"Idle - crouch_idle",
|
||||
"Action - spawn"
|
||||
],
|
||||
"id": "karth",
|
||||
"baseActorId": "karth",
|
||||
"fileName": "karth.glb",
|
||||
"url": "/assets/creatures/grafu-castle/karth.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/karth/act_karth_01.ros",
|
||||
"appearanceSignature": null,
|
||||
@@ -1019,6 +1272,221 @@
|
||||
"fileName": "milk-scarab-blue.glb",
|
||||
"url": "/assets/creatures/grafu-castle/milk-scarab-blue.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/mount_millenium_chimera/act_mount_millenium_chimera_laby.ros",
|
||||
"appearanceSignature": null,
|
||||
"sourceModelSha256": "1024bb257d901842c6bb7303eaba66399d26c28b680a7d8fadf4ee49c4b27a06",
|
||||
"checksum": "7cb4ae344d3a46094b96faebdc6274eace04eb894de64ee87ac7bb7a1309b321",
|
||||
"size": 1217348,
|
||||
"triangles": 3086,
|
||||
"bounds": {
|
||||
"min": [
|
||||
-0.4686,
|
||||
-0.01121,
|
||||
-2.26974
|
||||
],
|
||||
"max": [
|
||||
0.4686,
|
||||
1.73369,
|
||||
1.59992
|
||||
]
|
||||
},
|
||||
"groundOffset": 0.01121,
|
||||
"labelHeight": 2.0949,
|
||||
"markerRadius": 1.23829,
|
||||
"animations": [
|
||||
"Cast - casting01",
|
||||
"Cast - casting02",
|
||||
"Cast - casting_delay",
|
||||
"Terminal Pose - dead",
|
||||
"Death - death",
|
||||
"Action - dodge",
|
||||
"Wound - hurt",
|
||||
"Idle - idle02",
|
||||
"Idle - idle03",
|
||||
"Action - jump_down",
|
||||
"Action - jump_end_back",
|
||||
"Action - jump_end_run",
|
||||
"Action - jump_inplace",
|
||||
"Action - jump_loop",
|
||||
"Action - jump_up",
|
||||
"Run - run_back",
|
||||
"Run - run_forward",
|
||||
"Action - shuffle_left",
|
||||
"Action - shuffle_right",
|
||||
"Stand - stand_idle01",
|
||||
"Attack - unarmed_attack01",
|
||||
"Attack - unarmed_attack02",
|
||||
"Idle - unarmed_idle",
|
||||
"Walk - walk_forward"
|
||||
],
|
||||
"id": "mount-millenium-chimera-laby",
|
||||
"baseActorId": "mount-millenium-chimera-laby",
|
||||
"fileName": "mount-millenium-chimera-laby.glb",
|
||||
"url": "/assets/creatures/grafu-castle/mount-millenium-chimera-laby.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/enraged_big_bear/act_enraged_big_bear_purple.ros",
|
||||
"appearanceSignature": null,
|
||||
"sourceModelSha256": "22700bad0c13bad05bb722a7c6aa9959e4634217974b559901ab99e55fbfaca7",
|
||||
"checksum": "36738dc204f6d574f56ee41044d61ae88a885d0dc4b411f7ea70d76e84d48a4b",
|
||||
"size": 935312,
|
||||
"triangles": 1568,
|
||||
"bounds": {
|
||||
"min": [
|
||||
-1.65768,
|
||||
0.00083,
|
||||
-0.63438
|
||||
],
|
||||
"max": [
|
||||
1.66988,
|
||||
2.50467,
|
||||
0.55741
|
||||
]
|
||||
},
|
||||
"groundOffset": -0.00083,
|
||||
"labelHeight": 2.85384,
|
||||
"markerRadius": 1.06482,
|
||||
"animations": [
|
||||
"Cast - cast01",
|
||||
"Terminal Pose - dead",
|
||||
"Death - death",
|
||||
"Action - dodge",
|
||||
"Action - buff01",
|
||||
"Action - buff_sp02",
|
||||
"Wound - hurt",
|
||||
"Idle - idle02",
|
||||
"Idle - idle03",
|
||||
"Action - knockdown_end",
|
||||
"Action - knockdown_recover",
|
||||
"Run - run_back",
|
||||
"Run - run_forward",
|
||||
"Stand - stand_idle01",
|
||||
"Attack - unarmed_attack01",
|
||||
"Attack - unarmed_attack02",
|
||||
"Idle - unarmed_idle",
|
||||
"Action - unarmed_parry",
|
||||
"Walk - walk_forward",
|
||||
"Idle - knockdown_idle",
|
||||
"Cast - casting_delay",
|
||||
"Action - jump_down",
|
||||
"Action - jump_end_back",
|
||||
"Action - jump_end_run",
|
||||
"Action - jump_loop",
|
||||
"Action - jump_up"
|
||||
],
|
||||
"id": "enraged-big-bear-purple",
|
||||
"baseActorId": "enraged-big-bear-purple",
|
||||
"fileName": "enraged-big-bear-purple.glb",
|
||||
"url": "/assets/creatures/grafu-castle/enraged-big-bear-purple.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/enraged_big_bear/act_enraged_big_bear_cream.ros",
|
||||
"appearanceSignature": null,
|
||||
"sourceModelSha256": "e1d1810f37182d2f2c5e7dcf72aae4da75cbd5f056a5445df8dc17b2539d5bcb",
|
||||
"checksum": "1e327d246fdcebf9b35891f37ccc3aed3ff7bfb4f2c7355c1381d7e0231e956c",
|
||||
"size": 941160,
|
||||
"triangles": 1568,
|
||||
"bounds": {
|
||||
"min": [
|
||||
-1.65768,
|
||||
0.00083,
|
||||
-0.63438
|
||||
],
|
||||
"max": [
|
||||
1.66988,
|
||||
2.50467,
|
||||
0.55741
|
||||
]
|
||||
},
|
||||
"groundOffset": -0.00083,
|
||||
"labelHeight": 2.85384,
|
||||
"markerRadius": 1.06482,
|
||||
"animations": [
|
||||
"Cast - cast01",
|
||||
"Terminal Pose - dead",
|
||||
"Death - death",
|
||||
"Action - dodge",
|
||||
"Action - buff01",
|
||||
"Action - buff_sp02",
|
||||
"Wound - hurt",
|
||||
"Idle - idle02",
|
||||
"Idle - idle03",
|
||||
"Action - knockdown_end",
|
||||
"Action - knockdown_recover",
|
||||
"Run - run_back",
|
||||
"Run - run_forward",
|
||||
"Stand - stand_idle01",
|
||||
"Attack - unarmed_attack01",
|
||||
"Attack - unarmed_attack02",
|
||||
"Idle - unarmed_idle",
|
||||
"Action - unarmed_parry",
|
||||
"Walk - walk_forward",
|
||||
"Idle - knockdown_idle",
|
||||
"Cast - casting_delay",
|
||||
"Action - jump_down",
|
||||
"Action - jump_end_back",
|
||||
"Action - jump_end_run",
|
||||
"Action - jump_loop",
|
||||
"Action - jump_up"
|
||||
],
|
||||
"id": "enraged-big-bear-cream",
|
||||
"baseActorId": "enraged-big-bear-cream",
|
||||
"fileName": "enraged-big-bear-cream.glb",
|
||||
"url": "/assets/creatures/grafu-castle/enraged-big-bear-cream.glb",
|
||||
"rotationY": 0
|
||||
},
|
||||
{
|
||||
"sourceModel": "model/character/creature/rafael_bakderr/act_rafael_bakderr.ros",
|
||||
"appearanceSignature": null,
|
||||
"sourceModelSha256": "117287fd6f1ea813a73f846a0cde6bfd6d6f5ff00778dde82a1d7308aaafb0cc",
|
||||
"checksum": "06e87d84a7cb3e15d610b0f18528cb1331c80db4597c67c49267aa4a627386dc",
|
||||
"size": 1705044,
|
||||
"triangles": 3437,
|
||||
"bounds": {
|
||||
"min": [
|
||||
-0.59518,
|
||||
0.00039,
|
||||
-0.34992
|
||||
],
|
||||
"max": [
|
||||
0.59473,
|
||||
2.21216,
|
||||
0.25268
|
||||
]
|
||||
},
|
||||
"groundOffset": -0.00039,
|
||||
"labelHeight": 2.56177,
|
||||
"markerRadius": 0.4,
|
||||
"animations": [
|
||||
"Action - buff01",
|
||||
"Action - buff_sp01",
|
||||
"Cast - cast01",
|
||||
"Cast - cast_sp01",
|
||||
"Cast - casting_delay",
|
||||
"Action - continuous_buff",
|
||||
"Terminal Pose - dead",
|
||||
"Death - death",
|
||||
"Action - dodge",
|
||||
"Wound - hurt",
|
||||
"Idle - idle02",
|
||||
"Idle - idle03",
|
||||
"Run - run_forward",
|
||||
"Stand - stand_idle01",
|
||||
"Attack - unarmed_attack01",
|
||||
"Attack - unarmed_attack02",
|
||||
"Idle - unarmed_idle",
|
||||
"Action - unarmed_parry",
|
||||
"Walk - walk_forward"
|
||||
],
|
||||
"id": "rafael-bakderr",
|
||||
"baseActorId": "rafael-bakderr",
|
||||
"fileName": "rafael-bakderr.glb",
|
||||
"url": "/assets/creatures/grafu-castle/rafael-bakderr.glb",
|
||||
"rotationY": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Loaded 100 of 2481 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user