卫戍协议:盟约 web remake — initial import
Online co-op browser remake of Arknights' Stronghold Protocol: Alliance (client-simulated combat, official waves/bonds/items/strategies, 3D board, facing wheel, operator loadout, official emotes, deployment scripts).
This commit is contained in:
commit
01a44cd288
408 files changed
+308986
No files matched your search
@@ -0,0 +1,77 @@
|
||||
// Asset URL helpers (public/js/ui/assetUrls.js) against the real manifest: every visible chess resolves its
|
||||
// avatar and portrait (normal + golden), bonds / bands / items / enemies / factions / titles resolve, and
|
||||
// nothing outside the manifest is ever returned.
|
||||
|
||||
import { test, describe } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
uiUrl, chessAvatarUrl, chessPortraitUrl, skillIconUrl, profIconUrl, subProfIconUrl, bondIconUrl, bandIconUrl, itemIconUrl,
|
||||
enemyIconUrl, tokenAvatarUrl, factionIconUrl, titleIconUrl, effectIconUrl,
|
||||
} from '../../public/js/ui/assetUrls.js';
|
||||
|
||||
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
||||
const load = (f) => JSON.parse(readFileSync(path.join(ROOT, 'data', f), 'utf8'));
|
||||
const m = load('assets.json');
|
||||
const chess = load('chess.json');
|
||||
|
||||
function manifestUrls(obj, out = new Set()) {
|
||||
if (typeof obj === 'string' && obj.startsWith('/assets/')) out.add(obj);
|
||||
else if (obj && typeof obj === 'object') for (const v of Object.values(obj)) manifestUrls(v, out);
|
||||
return out;
|
||||
}
|
||||
const ALL = manifestUrls(m);
|
||||
const inManifest = (u) => u == null || ALL.has(u);
|
||||
|
||||
describe('assetUrls', () => {
|
||||
test('every visible chess (normal + golden) has avatar, portrait and skill icon', () => {
|
||||
let n = 0;
|
||||
for (const c of Object.values(chess)) {
|
||||
if (!c.charId || c.isDiy) continue;
|
||||
const a = chessAvatarUrl(m, c);
|
||||
const p = chessPortraitUrl(m, c);
|
||||
assert.ok(a && inManifest(a), `avatar ${c.chessId}`);
|
||||
assert.ok(p && inManifest(p), `portrait ${c.chessId}`);
|
||||
assert.ok(inManifest(skillIconUrl(m, c)), `skill ${c.chessId}`);
|
||||
assert.ok(inManifest(profIconUrl(m, c.profession)));
|
||||
assert.ok(inManifest(subProfIconUrl(m, c)));
|
||||
n++;
|
||||
}
|
||||
assert.ok(n >= 200);
|
||||
const golden = Object.values(chess).find((c) => c.isGolden && m.chars[c.charId]?.avatarE2);
|
||||
assert.equal(chessAvatarUrl(m, golden), m.chars[golden.charId].avatarE2, 'golden uses E2 art');
|
||||
});
|
||||
test('bonds, bands, items, enemies, tokens, factions, titles, effects', () => {
|
||||
for (const id of Object.keys(load('bonds.json'))) assert.ok(bondIconUrl(m, id), id);
|
||||
for (const id of Object.keys(load('bands.json'))) assert.ok(bandIconUrl(m, id), id);
|
||||
for (const it of Object.values(load('items.json'))) assert.ok(inManifest(itemIconUrl(m, it)), it.id);
|
||||
assert.ok(itemIconUrl(m, Object.values(load('items.json'))[0]));
|
||||
const enemies = load('enemies.json');
|
||||
let found = 0;
|
||||
for (const k of Object.keys(enemies)) { const u = enemyIconUrl(m, k); assert.ok(inManifest(u), k); if (u) found++; }
|
||||
assert.ok(found > Object.keys(enemies).length * 0.9);
|
||||
for (const t of Object.values(load('factions.json').types)) assert.ok(factionIconUrl(m, t.icon), t.type);
|
||||
for (const t of load('config.json').titles) assert.ok(titleIconUrl(m, t.picId), t.id);
|
||||
assert.ok(tokenAvatarUrl(m, 'token_10028_vigil_wolf'));
|
||||
assert.ok(effectIconUrl(m, { iconKind: 'team' }));
|
||||
assert.ok(effectIconUrl(m, { iconKind: 'choice' }));
|
||||
assert.ok(effectIconUrl(m, { iconKind: 'band', iconId: 'band_bldsk' }));
|
||||
assert.ok(effectIconUrl(m, { iconKind: 'garrison' }));
|
||||
assert.equal(effectIconUrl(m, { iconKind: 'x' }), null);
|
||||
assert.ok(uiUrl(m, 'hudPanel/icon_hp'));
|
||||
});
|
||||
test('missing manifest / ids ⇒ null, never throws', () => {
|
||||
for (const fn of [chessAvatarUrl, chessPortraitUrl, skillIconUrl, subProfIconUrl, itemIconUrl]) {
|
||||
assert.equal(fn(null, null), null);
|
||||
assert.equal(fn(m, { assets: {} }) ?? null, fn === skillIconUrl ? uiUrl(m, 'skillIcon/empty') : null);
|
||||
}
|
||||
assert.equal(bondIconUrl(m, 'nope'), null);
|
||||
assert.equal(bandIconUrl(null, 'band_bldsk'), null);
|
||||
assert.equal(enemyIconUrl(m, 'enemy_nope'), null);
|
||||
assert.equal(profIconUrl(m, 'NOPE'), null);
|
||||
assert.equal(uiUrl(undefined, 'x'), null);
|
||||
assert.equal(tokenAvatarUrl(m, null), null);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user