feedback1d WN: the local-client slug models are an optional overlay; the manifest never depends on the extraction
data/assets.json pointed 灼热源石虫 / 炽焰源石虫 at /assets/local/ files that only exist where tools/local-extract ran, with no fallback: without the extraction the asset tests failed, setup re-ran fetch-assets (rewriting the tracked manifest) and the slugs drew as icon diamonds. - The web model stays the 源石虫 alias (spine + spineAliasOf). The official model is enemies[id].spineLocal: file names in the data/local-assets.json group spine/enemy/<id> plus the parsed metadata, from the committed tools/assets/local-enemy-spines.json (fetch-assets --local-spines refreshes it from the extracted files, read only). The manifest is byte-identical with or without the extraction and has no /assets/local/ URL. - Client: assets.js spineEntry uses spineLocal only when the local manifest lists every file (createFieldView awaits assets.local() with the manifest); UnitView loads the web model when the local one fails. - extract.py writes the atlas with size: / pma: true itself (the same text as atlas.mjs normalizeAtlas), so the extraction is usable without a fetch pass. - setup notes when an older extraction lacks the enemy models.
This commit is contained in:
1 parent
f705797a86
commit
04634f5bf7
14 files changed
+633
-119
No files matched your search
+10
-1
@@ -2,7 +2,8 @@
|
||||
// data/assets.json. The pure helpers (animation-role resolver, atlas
|
||||
// normalizer, PNG/WOFF2/audio helpers, plan id sets) are always tested; the
|
||||
// on-disk checks run only when public/assets exists (it is git-ignored and
|
||||
// produced by `npm run assets`).
|
||||
// produced by `npm run assets`); the optional local-client enemy models
|
||||
// (spineLocal, tools/local-extract) only when data/local-assets.json lists them.
|
||||
|
||||
import { test, describe } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
@@ -19,6 +20,7 @@ import { assetToPath, pickUnitSfx, indexAudio } from '../tools/assets/audio.mjs'
|
||||
import { mirrorUrl, safeName, encodePath } from '../tools/assets/sources.mjs';
|
||||
import { collectEnemyIds, skillIndicesByChar } from '../tools/assets/plan.mjs';
|
||||
import { resolveTemplate, collectLeaves } from '../tools/assets/manifest.mjs';
|
||||
import { spineEntry } from '../public/js/assets.js';
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const PUBLIC = join(ROOT, 'public');
|
||||
@@ -583,6 +585,13 @@ describe('generated manifest data/assets.json', () => {
|
||||
for (const e of Object.values(manifest.enemies)) if (e.spine) models.set(e.spine.skel, e.spine);
|
||||
for (const t of Object.values(manifest.tokens)) if (t.spine) models.set(t.spine.skel, t.spine);
|
||||
assert.ok(models.size > 400);
|
||||
// the official enemy models of the local client, as the client resolves them (DESIGN §13: only when listed)
|
||||
const localPath = join(ROOT, 'data', 'local-assets.json');
|
||||
const local = existsSync(localPath) ? JSON.parse(readFileSync(localPath, 'utf8')) : null;
|
||||
for (const id of Object.keys(manifest.enemies)) {
|
||||
const s = local ? spineEntry(manifest, id, { local }) : null;
|
||||
if (s?.local && [s.skel, s.atlas, ...s.textures].every((u) => existsSync(join(PUBLIC, u)))) models.set(s.skel, s);
|
||||
}
|
||||
for (const s of models.values()) {
|
||||
const dir = dirname(s.atlas);
|
||||
// pixi-spine finds the atlas by swapping the extension of the skel URL
|
||||
|
||||
Reference in new issue
Block a user