0.2.5-dev: merge claude/036-scene

# Conflicts:
#	CHANGELOG.md
#	docs/history/0.2.5.md
This commit is contained in:
sganggs committed 2026-10-12 15:00:10 +08:00
commit bb88031256
19 files changed
+1660 -22

No files matched your search

+1
View File
@@ -165,6 +165,7 @@ The research JSONs in `docs/research/` (03, 05, 07) define **which** ids are nee
| Enemy Spine (PC build, premultiplied alpha) | isHarryh/Ark-Models `models_enemies/{key}/`, file names from `models_data.json` | `spine/enemy/{enemyId}/{stem}.*` |
| Enemy Spine that no dump carries (灼热源石虫 / 炽焰源石虫) | the local client only (`tools/local-extract/extract.py ENEMY_SPINES`, optional); never downloaded and never required: an overlay of the web alias (`enemies[id].spineLocal`) | `local/spine/enemy/{enemyId}/{stem}.*` (listed in `data/local-assets.json`) |
| Original token Spine overlays (40 summons: most 自选 summons, 凯瑟琳's 爬行号·防护单元, 凛御银灰's 风雪之眼, 香槟炸弹) | local-client extraction (`tools/local-extract/extract.py TOKEN_SPINES`, optional); the original overlay is preferred, with any listed web model retained as fallback | `local/spine/token/{tokenId}/{stem}.*` (listed in `data/local-assets.json`) |
| Official battlefield scenes (one per battlefield: meshes, lightmaps, albedo textures; DESIGN §15, DESIGN §30.17) | the scene bundles `scenes/activities/<season>/level_<stage>/…` (`tools/local-extract/extract.py --only scene`, optional). The client carries them only while the season runs; off season `tools/local-extract/fetch_scenes.py` downloads the bundles of the in-season version `26-04-13-12-31-42_e9b560` from the official CDN into `.cache/official-scenes/` (33 bundles, 22,801,308 bytes compressed on the Android list, md5-checked), and the materials / textures still come from the local client's theme bundles. The 0.2.5 full release zip includes the extracted files under `public/assets/local/scene/` when the maintainer extracted them before packaging; a lite zip and a source install may fetch and extract them (`node tools/setup.mjs --scenes`). The repository never stores the bundles or the extracted files. Without a scene the 3D board draws its procedural metal tiles | `local/scene/{stageId}/scene.json`, `scene.bin`, `lm{i}.*`; `local/scene/tex/{texture}.*` (listed in `data/local-assets.json`) |
| BGM | AA2 `voice` branch `audio/sound_beta_2/music/**` (大厅/休整期 `act1autochess`, 开战 `act13side/m_bat_kazimierz2_{1,2}` — 骑士之日 / 无畏者; the 开战 track follows the round: `_2` 无畏者 rounds 1–7, `_1` 骑士之日 from round 8) | `audio/bgm/{file}.mp3` |
| SFX (UI, battle, per unit) | AA2 `voice` `audio/sound_beta_2/**`, mapped from `audio_data.json` banks | `audio/sfx/{same sub-path}.mp3` |
| 干员战斗语音 | AA2 `voice` `audio/sound_beta_2/voice_cn/{charId}/cn_nn.mp3` (Chinese, `audio.voice`) and `voice/{charId}/cn_nn.mp3` (Japanese, `audio.voiceJp`: the same file names) — the lines `charword_table.json` lists (`placeType` = when the game plays one, `voiceAsset` = the path); `--voice-lang=jp|en|kr` fills `audio.voice` from `voice/`, `voice_en/`, `voice_kr/` instead | `audio/voice/cn/{charId}/{cn_nn}.mp3`, `audio/voice/jp/{charId}/{cn_nn}.mp3` (`audio/voice/{lang}/…` for another `--voice-lang`) |
+5
View File
@@ -300,6 +300,7 @@ services:
| 内容 | 没有本地素材时 |
|---|---|
| 官方 3D 棋盘(贴图、模型、地图特效) | 2D 棋盘,地块由程序绘制 |
| 各战场的官方场景(沙地等 11 个战场,0.2.5 素材包;完整包在提取后带上,精简包和源码可在本机下载并提取,见下) | 3D 棋盘用程序生成的金属地块 |
| 部分官方界面图标与底板:交流按钮和表情面板的边框、暂停面板、装备替换窗口、干员调配界面、队友状态与漏怪标记、模组类型图标等 | 样式相近的替代图形、图标或文字 |
| 灼热 / 炽焰源石虫的官方模型 | 染成橙色 / 红橙色的普通源石虫 |
| 39 个召唤物的官方模型(多数自选召唤物,以及凯瑟琳的爬行号·防护单元、凛御银灰的风雪之眼;公开镜像没有) | 召唤物头像(菱形底板) |
@@ -310,6 +311,8 @@ services:
**0.2.0 之前提取过的**:召唤物模型是 0.2.0 新增的提取项,旧的提取结果里没有(`node tools/setup.mjs` 会提示「缺少新版的自选召唤物模型」)。有客户端的电脑运行 `node tools/setup.mjs --local` 重新提取即可,只想补这一项也可以在提取用的 Python 环境里运行 `tools/local-extract/extract.py --only spine/token`(新文件写入 `public/assets/local/spine/token/`,清单里其他条目保持不变)。
**各战场的官方场景**(0.2.5 素材包,维护者 2026-10-12:可以进完整包;精简包和源码安装可以在本机下载):官方棋盘是每个战场一个做好的场景(11 个战场,其中 9 个是沙地)。场景包只在赛季开放期间随客户端分发,赛季外的客户端里没有。仓库不存放这些官方文件。有客户端的电脑可以 `node tools/setup.mjs --scenes`(询问或强制下载并提取;`--no-scenes` 跳过)。也可以分两步:`python3 tools/local-extract/fetch_scenes.py`(从官方 CDN 下载赛季版本 `26-04-13-12-31-42_e9b560` 的 33 个场景包,共 22,801,308 字节,放进 `.cache/official-scenes/`;`--list` 只看清单不下载),再用提取环境的 Python 运行 `tools/local-extract/extract.py --only scene`(材质和贴图仍取自本机客户端;`--game` 可指定客户端目录)。没有 Python 3.8+,或 UnityPy / lz4 / Pillow 没装好时,setup 只提示并继续,3D 棋盘用金属地块。没有客户端时贴图取不出来,从同一版本的完整包复制 `public/assets/local/` 和 `data/local-assets.json`。作者估计每个场景在进对局时另下载约 2–7 MB,各场景共用贴图约 3 MB;本机没有跑过提取,完整包因此增加的解压体积没有实测。
**3D 棋盘贴图的下载量**:每位玩家进入对局时都要从开服的电脑下载 3D 棋盘的 12 张贴图。提取时会给这 12 张各写一份 WebP(颜色贴图有损、质量 95,法线和数据贴图无损),清单里列的是 WebP,同名 PNG 留在旁边给裁切工具和 setup 用。这部分下载量从约 6.7 MB 降到约 2 MB,网速慢的远程联机最明显。只有 PNG 的本地素材(例如在这一改动之前提取的)可以用提取时的 Python 环境运行 `tools/local-extract/extract.py --webp` 就地补上,只需要 Pillow,不需要客户端。
## 7. 打包发布(维护者)
@@ -323,6 +326,8 @@ npm run package:lite -- --out <目录> # 精简包 Stronghold-Protocol-v<版
npm run package -- --update --from <旧版本的完整包>[,<…>] --out <目录> # 更新包 Stronghold-Protocol-v<版本>-update.zip
```
完整包在打包机已经提取战场场景时带上 `public/assets/local/scene/`(`--dry-run` 摘要里的 `battlefield scenes` 一行)。还没有提取时完整包仍能打,棋盘用金属地块。要带上 11 个官方场景,先运行 `python3 tools/local-extract/fetch_scenes.py`,再用提取环境的 Python 运行 `tools/local-extract/extract.py --only scene`(或 `node tools/setup.mjs --scenes`),然后再 `npm run package`。精简包不带场景。`.cache/official-scenes/` 在拒绝名单里,不会打进任何包。`tools/local-extract/scenes.py` 和 `fetch_scenes.py` 必须先提交:打包读的是 `git ls-files`,未提交的脚本不会进玩家包。
- **打进去的**:`git ls-files` 里的 `server/`、`shared/`、`data/`、`public/`(不含 `public/dev/`)、`packs/`(随仓库提交的内容包;只在本机安装、没提交的不打进去)、启动脚本、玩家会运行的工具(setup、vendor、fetch-assets 与 `tools/assets/`、doctor,以及 setup 调用的 `tools/local-extract/` 和 `crop-board-atlas.mjs`)、服务器和 fetch-assets 读取的 4 张研究数据表(`docs/research/` 的 `03-operators`、`05-enemies`、`05-maps`、`07-assets` 四个 JSON)、`package.json` / `package-lock.json`、许可证与说明(`LICENSE`、`NOTICE.md`、`THIRD-PARTY-NOTICES.md`、`README.md`、`CHANGELOG.md`)、`docs/PLAYING.md` 和本文;然后在临时目录里生成 `packs/index.json`(打进去的语言包和内容包的列表,供纯静态托管使用;服务器自己会实时列出,见 [PACKS.md](PACKS.md)),再 `npm ci --omit=dev` 装上运行依赖和 `public/vendor`。完整包再加上 `data/assets.json` 列出的素材、`public/fonts`,以及本地提取的 `public/assets/local/` 和 `data/local-assets.json`。磁盘上有、清单却没列出的文件不打进去(例如 0.2.0 移出自选的焰狐龙梓兰的旧素材)。日文干员语音(`audio.voiceJp`,`public/assets/audio/voice/jp/` 的 2674 个文件,约 85 MB,zip 后约 76 MB)默认也打进完整包;`tools/package.mjs` 里的开关 `FULL_ZIP_JP_VOICE` 改成 `false` 时,完整包(以及由它比较出的更新包)不带这些文件:玩家首次启动时 setup 会像精简包那样下载它们,下载完成前选「日本語」会播中文语音,更新包也不会删除玩家已有的日文语音。
- **不打进去的**:`test/`、维护用的工具(数据构建、golden、botbench、i18n、导入检查、本工具等)、`scripts/make-windows-bundle.mjs`(Windows 便携包,见 [WINDOWS.md](WINDOWS.md))、其他文档、研究笔记和 `docs/img/`、`handoff/`、`.github/`、`types/`、lint / 编辑器 / Docker 配置。和 0.1.x 的整树打包(全部跟踪文件加上 `public/assets` 的全部内容)相比,0.2.0 的完整包少了约 640 个文件、解压后小约 26 MB,zip 小约 8 MB。
- **打包前的检查**(`--dry-run` 也全部做一遍):拒绝名单(`pv`、`review`、`.cache`、`.claude`、`.git`、`logs`、`.env`、`scripts/service.env.cmd`、`handoff`、`test` 等);每个打进去的模块的相对导入、玩家用的 npm 脚本(start / setup / doctor / launch / postinstall / vendor / assets)都指向包里的文件;完整包里 `data/assets.json` 和 `data/local-assets.json` 列出的文件都在(缺了先运行 `node tools/fetch-assets.mjs`);没有只差大小写的两个路径;包里的文件(二进制素材也查)不含个人目录路径(`/Users/…`、`C:\Users\…`、`/home/…`)或本机的账户名(运行时从系统读取;`SP_PACKAGE_SCAN_NAMES=a,b` 可以再加名字);打进去的已跟踪文件没有未提交的改动(重新生成的 `data/assets.json` 要先提交)。有任何问题都会列出原因、以非零状态结束,不写 zip;正式打包时还会核对临时目录里的文件和计划完全一致。
+1 -1
View File
@@ -5,7 +5,7 @@ This is the **single source of truth** for every implementer. Research lives in
Language: player-facing text is **Simplified Chinese** by default, with an English switch since 0.2.0 (§25.2, docs/I18N.md: UI strings through `t('中文')`, game texts from the official Chinese data or the official EN client's). Code, comments and identifiers are English.
Current development state: **0.2.5-dev**, §30, the state described by this document. Its fixed gifts, already displayed operator offers, combat corrections
and client/tool changes supersede the corresponding 0.2.4 rules below. It is not yet released.
and client/tool changes supersede the corresponding 0.2.4 rules below. Official battlefield scenes (DESIGN §15, DESIGN §30.17) are optional 0.2.5 local art: the full bundle includes them when extracted, and a missing scene keeps the metal board. It is not yet released.
Versions: the first public release was **0.1.0** (2026-10-02, the state of §0–§20.15); the releases after it add the player feedback and GitHub reports — 0.1.1 §21, 0.1.2 §22, 0.1.3 §23, 0.1.4 §24 — and **0.2.0** (2026-10-07; `CHANGELOG.md`) the maintainability refactor, two languages, 补位, 自选编队 and their fidelity work — §25 — **0.2.1** (2026-10-07) the 联防 battlefield restored, full potential and the GitHub fixes after it — §26 — and **0.2.2** (2026-10-09) per-operator 潜能 / 练度, Japanese voices, the statistics page, 失衡, whole-frame attack timing and the GitHub fixes after 0.2.1 — §27. **0.2.3** (2026-10-10; `package.json`, `shared/constants.js APP_VERSION`) 克莱门莎 and new modules, community fixes, text size, per-operator voices, installation, the opening reroll and local match recovery — §28. **0.2.4** (2026-10-11) the official shared card pool and item stock, Raid revival and #475, and the community fixes after 0.2.3 — §29. The labels v1 / v2 / v2.1–v2.5.2 in §0, §14–§20 and in the BALANCE / SIM comparisons name the design generations and the private playtest builds that came before it; they are kept as history.
+6 -1
View File
@@ -63,7 +63,8 @@ Audio: autochess BGM per phase, UI SFX (buy/sell/refresh/level/merge/ready/timer
## 13. Local-client art (optional, host-side)
`tools/local-extract/extract.py` (Python: UnityPy + lz4 + Pillow; decodes Arknights' custom **LZ4AK** bundle compression via `aklz4.py`) extracts the art the web download does not include (some of it, most official HUD sprites among it, is on the public mirror too — §22.5) from a locally installed client (CrossOver Windows build or PlayCover iOS build) into `public/assets/local/**` and writes `data/local-assets.json` (`{ groups: { '<subdir>': { name: { path, w, h, kind } } } }`). Everything here is **optional**: clients must render correctly when the file or any entry is missing (fall back to the web assets / procedural art). The emotes and the guide pages below are on the public mirror too (§22.5): fetch-assets downloads them (data/assets.json `ui['emoticon/<dir>/<picId>']`, `ui['guide/<key>']`) and the client shows the local copy first, then that one (`data.js artUrls`); docs/DEPLOY.md §6 lists what still needs the client.
- `map/autochess/TX_autochessi_*` — the real autochess board atlas (`_D` diffuse 2048² with floor tiles, gold-framed high-ground plates, REINFORCEMENTS bench pads, EQUIPMENTS pads, crates, gates; `_N/_M/_E` normal/mask/emission; `common_D` device icons; `BG` backdrop). `map/autochesssand/*` — the sand-theme variant.
- `map/autochess/TX_autochessi_*` — the real autochess board atlas (`_D` diffuse 2048² with floor tiles, gold-framed high-ground plates, REINFORCEMENTS bench pads, EQUIPMENTS pads, crates, gates; `_N/_M/_E` normal/mask/emission; `common_D` device icons; `BG` backdrop). `map/autochesssand/*` — the textures and materials of the sand theme (the theme of nine of the eleven battlefield scenes, §15).
- `scene/<stageId>/` and `scene/tex/` — the official scene of a battlefield (§15 "Official battlefield scenes", DESIGN §30.17): `scene.json` (materials, key light, chunk records), `scene.bin` (the meshes in board space), the lightmap pages `lm<i>`, and the materials' albedo textures shared by the scenes. Read from the scene bundles `scenes/activities/<season>/level_<stage>/…`, which the client carries only while the season runs: `extract.py --only scene` takes them from the client or from `--scenes <dir>` / `.cache/official-scenes/*` (`tools/local-extract/fetch_scenes.py` downloads them from the official CDN's in-season version). The 0.2.5 full release zip includes the extracted files when the maintainer extracted them before packaging; a lite zip and a source install may fetch and extract them (`node tools/setup.mjs --scenes`). The repository never stores the bundles or the extracted files.
- `ui/{common,battle,outer}/*` — every sprite of the three official autochess UI bundles (tier chips I–VI, bond disc rings, frames, banners, HUD pieces, badges, backgrounds, mode art).
- `emoticon/<dir>/<picId>.png` — the 36 official battle emotes, 6 themes (`shared/constants.js → EMOTE_THEMES / EMOTES`, `emoteArtPath`; data/emotes.json); also downloaded from the mirror (§22.5).
- `guide/autochess_{home,shop,handbook}_N.png` — the 19 official tutorial pages; stored squashed to 1024² — **display at 16:9** (the mirror copies too, §22.5).
@@ -78,6 +79,10 @@ Audio: autochess BGM per phase, UI SFX (buy/sell/refresh/level/merge/ready/timer
Findings (2026-09-28): 卫戍协议 levels have no scene bundle (`mapId` null; the official hot-update list contains none). The client **builds the board at runtime** from the stage grid using the map theme `arts/maps/map_autochess` (materials `MT_autochess` (albedo `TX_autochessi_D` 2048², normal `_N`, metallic/gloss `_M`, emission `_E`), `MT_autochess_common` (`common_D/_E`: device/gate icons), `MT_autochess_Transparent` (`Transparent_D/N/M/E`), `MT_autochessi_BG` (`TX_autochessi_BG`)), the background meshes `arts/maps/map_autochess/bkg_mesh.ab` (`S_Background_common`, `S_Background_shadow`) and `arts/maps/common/meshes/s_background_common.ab`, device meshes (`s_common_box_01` crate, `s_wind_device` blower, …), plus the standard gate/objective effects. The level's `options.configBlackBoard` carries the official camera framing (`default_pos_x/y`, `default_size`, `enemy_pos_x/y`, …).
Correction (2026-10-11, GitHub #532; PR #533 by @andrew05060414): the first finding above was made off season and is wrong. While a season runs, the official hot-update list carries **one prebuilt scene per battlefield** — `scenes/activities/act1autochess/level_act1autochess_m01…m07` and `scenes/activities/act2autochess/level_act2autochess_m01…m04` (each with `…/lightingdata.ab` and `…_lighting_settings.ab`; Android versions `25-11-21-15-21-44_ee1197`, `26-03-13-06-11-47_14fb99`, `26-04-13-12-31-42_e9b560`); the lists of 2026-09-13 and 2026-10-08 have none, and no 卫戍协议 level data either. The asset index of those versions (`<hash>.idx`: each bundle's dependencies) gives each scene's art: act1 m01–m07 and act2 m02 / m04 depend on `arts/maps/map_autochesssand` (m05 and act2 m04 also on `map_doss_shore`), act2 m01 on `map_lakegarden` + `map_syracuse_metro`, act2 m03 on `map_kxmr` + `map_lm_center`. **No battlefield scene depends on `arts/maps/map_autochess`** (the metal theme the procedural board below draws; in the current client the 矢量突破 scenes `act3break` use it). A scene holds the logic tiles (`(row,col)#tileKey` at anchor-local (col, row, −height), high ground 0.4), the art under `GraphicHolder` (static parts merged into "Combined Mesh (root: scene)" objects, lightmapped: Shadowmask mode, one directional key light), and three sub-graphics `HIDDEN@1…3` (`MapSubGraphicHolder`) that the game switches on: the left normal field, the right normal field and the boss field — the areas of `layout.js AREAS`.
Official battlefield scenes (`board3d/load.js loadScenePack`, `scene.js setScenePack`, `materials.js sceneMaterial`): when `data/local-assets.json` lists `scene/<stageId>`, the 3D board draws that scene instead of the procedural tiles, cliffs and background plane; the devices, gate / objective boxes, field edges and terrain overlays stay as below. The base part is always drawn; a `HIDDEN@n` part is drawn when its centre lies in one of the built areas. Without the scene (not extracted, a load failure, an unknown `version`) the procedural metal board is drawn as before. Approximations: the official shaders are not reproduced — a lit material is albedo × (the lightmap's baked light + key light · N·L · the baked shadow mask), in linear light; normal, metallic and emission maps, the vertex-colour blend (`_HG_VERTEX_COLOR_BLEND_ON`), the sky box, the map effects (`mapeff_*`) and the post-processing profile are not drawn; `Torappu/Scene/StylizedWater` is the board's water shader in the material's colours; crates cast no shadow on a scene. **[ASSUMED]** `SCENE_LIGHT` (`direct` 0.8, `indirect` 0.4, `ambient` 0.2: tuned by eye against the PRTS screenshot of 战场#06, the official post-processing being unknown) and the RGBM reading of the lightmap pages (`scenes.py decode_rgbm`: Unity's linear-space decode, range 5). Owner decision (2026-10-12): these extracted files belong to the 0.2.5 asset pack and may ship in the full release zip; a lite package or a source install may download and extract them on that machine. Missing extractor dependencies keep the metal board. The repository still never stores the official files.
Target: a **real 3D board layer** rendered with three.js (vendored ESM, no CDN) underneath the existing Pixi layer:
- Geometry generated from `data/stages.json` like the official builder: LOW tiles = top quads; HIGH tiles = raised blocks with side walls; forbidden/void, fences, gates (red), objective (blue), hand/temp pads, special terrain; UVs mapped onto the official atlas regions (reuse/extend `public/assets/local/map/autochess/tiles.json`); normal + emission + metallic maps; lighting tuned to match official screenshots (key directional light, ambient/hemisphere, soft contact shadows); official background meshes & texture; device meshes (OBJ exported by `tools/local-extract`) with their textures; animated gate/objective effects.
- One camera model drives both layers: `public/js/render/projection.js` keeps its public API (`worldToScreen`, `screenToWorld`/tile picking, presets, transitions) but derives it from a three.js `PerspectiveCamera` initialised from the official configBlackBoard framing, so Spine units, FX, drag/drop and hit-testing stay aligned with the 3D board.
+48
View File
@@ -366,3 +366,51 @@ replace 设置 → 问题反馈.
Tests: `test/sim/official-damage-numbers.test.js`, `test/ui/official-damage-numbers.test.js`,
`test/render/official-numbers.test.js`. Golden digests do not move: no new event kind is emitted, and the extra
tuple field is not part of the digest. Player notes: PLAYING; client notes: design/client.md.
### 30.17 Official battlefield scenes (#532)
Report: the web board draws the metal theme for every battlefield. The reporter asked for the sand theme.
PR #533 by @andrew05060414.
Findings (2026-10-11; the official hot-update lists and asset indexes, read from the CDN, and the scene bundles of
version `26-04-13-12-31-42_e9b560`):
- The official board is not assembled at run time. Every battlefield is a prebuilt scene bundle, shipped only while
the season runs. DESIGN §15 said otherwise from an off-season list and is corrected there.
- Which art a battlefield uses is the dependency list of its scene bundle: the sand theme for act1 m01–m07 and act2
m02 / m04, `map_lakegarden` + `map_syracuse_metro` for act2 m01, `map_kxmr` + `map_lm_center` for act2 m03. The
same in the 上半 version of 2025-11 and the 下半 versions of 2026-03 / 04. No battlefield uses the metal theme.
- The sand theme bundle holds no mesh because the models are baked into each scene ("Combined Mesh (root: scene)").
- A scene's three sub-graphics `HIDDEN@1…3` are the left normal field, the right normal field and the boss field.
- `python3 tools/local-extract/fetch_scenes.py --list` against that Android hot-update list names 33 bundles and
22,801,308 bytes. That is the compressed CDN download, not the extracted size in the full zip.
Change: `tools/local-extract/scenes.py` exports a battlefield's scene (meshes in board space, materials, key light,
lightmap pages with the baked shadow mask in alpha, albedo textures) to `public/assets/local/scene/<stageId>/`;
`extract.py --only scene` runs it, reading the scene bundles from the client, `--scenes <dir>` or
`.cache/official-scenes/*`; `tools/local-extract/fetch_scenes.py` downloads the in-season bundles from the official
CDN (md5-checked against that version's hot-update list). The 3D board draws a stage's scene when the local-art
manifest lists it (`board3d/load.js loadScenePack`, `scene.js setScenePack`); the procedural metal board stays the
fallback and still draws the devices, gates, field edges and terrain overlays.
Owner decision (2026-10-12): the eleven scenes belong to the 0.2.5 asset pack. The maintainer's full release zip
includes the extracted files. A lite package or a source install may download and extract them on that machine
(`node tools/setup.mjs --scenes`; `--no-scenes` skips). Missing Python 3.8+ or UnityPy / lz4 / Pillow is a warning,
and the metal board remains. The repository never stores the bundles or the extracted files (`.gitignore` already
ignores `public/assets/`, `data/local-assets.json` and `.cache/`, and names `.cache/official-scenes/`). Packaging
does not fail when the scenes are absent; the summary line `battlefield scenes` says whether this machine has them.
`scenes.py` and `fetch_scenes.py` ship in a player zip only after they are committed, because the packager reads
`git ls-files`.
No gameplay change: the scene is art under the same grid, camera and unit layer. Golden results are not moved by
this change.
**[ASSUMED]** the light gains `SCENE_LIGHT` (`range` 8.0, `ambient` 0.2, `direct` 0.8, `indirect` 0.4) and the RGBM
reading of the lightmap pages (Unity linear decode, range 5), both in DESIGN §15. Not done: the scene's normal /
metallic / emission maps, map effects, sky box and post-processing; the tile graphics a level switches at run time
(mire, tide) still use the remake's overlays. The extracted byte size inside a full zip was not measured here.
Tests: `test/render/board3d-scene.test.js` (the scene table against data/stages.json, board-space conversion, chunk
layout shared by scenes.py and load.js, sub-graphic groups, RGBM decode, the loader with a fake store, BoardScene
with a stub renderer: scene on / areas / fallback). `test/scene-assets.test.js` (setup's scene step, the full plan
ships extracted scene files, the lite plan ships none, git ignores the bundles).