feedback1d WN: the web alias of 灼热/炽焰源石虫 is drawn tinted toward their own colours; bundle step documented
This commit is contained in:
1 parent
3cdf7182f2
commit
a703800386
3 files changed
+56
-6
No files matched your search
+6
-2
@@ -226,8 +226,12 @@ Other renderer rules from research 07 §5.4–5.5:
|
||||
the extraction, it has no `/assets/local/` URL, and setup / doctor / the manifest tests never miss these files.
|
||||
- The client (`assets.js spineEntry`, with `assets.local()`, which `createFieldView` awaits with the manifest) draws
|
||||
the official model when the local manifest lists every file of it; otherwise, or when it fails to load
|
||||
(`UnitView`: the entry's `fallback`), the web alias. A release bundle carries the models when it is zipped from a
|
||||
checkout where the extraction ran.
|
||||
(`UnitView`: the entry's `fallback`), the web alias, tinted toward the slug's own lava colours
|
||||
(`render/units.js ALIAS_TINT`: 灼热 orange, 炽焰 red-orange; research 07 §5.6 "a hue shift", [ASSUMED] look) so a
|
||||
source install without the extraction still tells them from the plain 源石虫. A release bundle carries the
|
||||
models only when it is zipped from a checkout where the extraction ran with the `spine/enemy` job (an extraction
|
||||
made with 0.1.0 lacks it: `node tools/setup.mjs --local` again, then check that `data/local-assets.json` lists
|
||||
`spine/enemy/enemy_1305_mhslim` and `spine/enemy/enemy_1305_mhslim_2`).
|
||||
User feedback after 0.1.0 (D3: "所有特殊源石虫的模型全表现为普通源石虫"): the ELEMENT faction spawns up to ten of them a
|
||||
round. A 2026-10-03 audit of every enemy of `data/enemies.json` (249) against the client's battle prefabs (the
|
||||
skeleton each prefab's Spine renderer draws) found no other enemy drawn with another enemy's model; 伊利昂的木驮兽
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
// with ready glow / draining skill bar, tier chip, status icons, blocked marker). The fallback shows at once and
|
||||
// cross-fades to the Spine model when it has loaded; missing or failed models keep the fallback forever (the
|
||||
// game never blocks on Spine) — except an optional local-client model (assets.js spineEntry `fallback`, DESIGN §13:
|
||||
// 灼热源石虫 / 炽焰源石虫), which falls back to the web model first. Every bar is a tinted Texture.WHITE sprite, so
|
||||
// HUDs batch into few draw calls.
|
||||
// 灼热源石虫 / 炽焰源石虫), which falls back to the web model first; that web alias (the plain 源石虫) is drawn tinted
|
||||
// toward the slug's own colours (ALIAS_TINT). Every bar is a tinted Texture.WHITE sprite, so HUDs batch into few draw
|
||||
// calls.
|
||||
//
|
||||
// Placement: feet anchored at world (x, y, z); scale = camera px-per-tile at the feet × UNIT.modelScale, so
|
||||
// chibis shrink with distance like the original. An enemy's model is also scaled by its official prefab factor
|
||||
@@ -94,6 +95,13 @@ export const DOWN_LOOK = Object.freeze({
|
||||
ring: Object.freeze({ [DOWN_STATE.COUNTING]: 0x4ed8af, [DOWN_STATE.WAIT_DP]: 0xffc600, [DOWN_STATE.WAIT_TILE]: 0xff4b3e }),
|
||||
size: 0.42, height: 1.02,
|
||||
});
|
||||
/**
|
||||
* An enemy drawn with another enemy's web model because its own is only in the local client (manifest `spineAliasOf`
|
||||
* + `spineLocal`: 灼热 / 炽焰源石虫 → the plain 源石虫's skeleton, feedback D3 after 0.1.0): a multiply tint toward its own
|
||||
* lava colours (orange / red-orange), so it reads apart from the plain slug where the local art was not extracted
|
||||
* (research 07 §5.6 "a hue shift") [ASSUMED look]. Never on its official (local) model; status tints win over it.
|
||||
*/
|
||||
export const ALIAS_TINT = Object.freeze({ enemy_1305_mhslim: 0xffc48a, enemy_1305_mhslim_2: 0xff9070 });
|
||||
/**
|
||||
* Element gauge row under the bars (see header): the disc's diameter in tiles and its pixel clamp (enemies × `enemy`),
|
||||
* the gap under the bars (px). The white bar is as tall as the SP bar and fills the rest of the bars' width. During a
|
||||
@@ -255,6 +263,7 @@ export class UnitView {
|
||||
this.body.addChild(this.fallback);
|
||||
this.actor = null;
|
||||
this.spineReady = false;
|
||||
this.baseTint = 0xffffff; // the drawn model's own tint (ALIAS_TINT), under the status tints
|
||||
|
||||
this.hud = new P.Container();
|
||||
ctx.layers.bars.addChild(this.hud);
|
||||
@@ -340,6 +349,7 @@ export class UnitView {
|
||||
if (swap) this._dropActor();
|
||||
this.actor = actor;
|
||||
this._actorEntry = entry;
|
||||
this.baseTint = (!entry.local && ALIAS_TINT[id]) || 0xffffff; // the web alias of a local-only model
|
||||
this.body.addChild(this.actor.spine);
|
||||
this.spineReady = true;
|
||||
this.swapT = swap ? 1 : 0;
|
||||
@@ -704,7 +714,7 @@ export class UnitView {
|
||||
this.fallback.visible = this.swapT < 1;
|
||||
const sc = s * UNIT.modelScale * this.modelK;
|
||||
const flashK = this.flash > 0 ? this.flash : 0;
|
||||
let tint = 0xffffff;
|
||||
let tint = this.baseTint;
|
||||
if (this.down) tint = DOWN_LOOK.tint;
|
||||
else if (this.flags & UF.FROZEN) tint = 0x9fd4ff;
|
||||
else if (this.flags & UF.COLD) tint = 0xcfe6ff;
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
// tools/assets/local-enemy-spines.json — never from the disk, so the manifest is the same with or without the
|
||||
// extraction). The client (assets.js spineEntry) draws the official model when data/local-assets.json lists every one
|
||||
// of its files and falls back to the web model when it fails to load (DESIGN §13: local art is optional).
|
||||
// Without the extraction the web alias is drawn tinted toward the slug's own colours (render/units.js ALIAS_TINT,
|
||||
// research 07 §5.6 "a hue shift" [ASSUMED look]), so source installs still tell them apart from the plain slug.
|
||||
// 高能 / 冰爆 / 简饲源石虫 and “庞贝” always had their own models (checked in headless Chrome).
|
||||
|
||||
import { test, describe, before, after } from 'node:test';
|
||||
@@ -207,7 +209,12 @@ describe('D3 client: the official model only when data/local-assets.json lists i
|
||||
|
||||
describe('UnitView', () => {
|
||||
let fake, UnitView;
|
||||
before(async () => { fake = installFakePixi(); ({ UnitView } = await import('../public/js/render/units.js')); });
|
||||
let ALIAS_TINT, UF;
|
||||
before(async () => {
|
||||
fake = installFakePixi();
|
||||
({ UnitView, ALIAS_TINT } = await import('../public/js/render/units.js'));
|
||||
({ UF } = await import('../shared/constants.js'));
|
||||
});
|
||||
after(() => fake.restore());
|
||||
const tick = () => new Promise((r) => setImmediate(r));
|
||||
const cam = () => presetCamera('normal', { width: 1280, height: 720 });
|
||||
@@ -238,6 +245,35 @@ describe('D3 client: the official model only when data/local-assets.json lists i
|
||||
assert.equal(bad.actor?.entry, web, 'drawn with the web model');
|
||||
assert.equal(bad.entry, web);
|
||||
});
|
||||
|
||||
test('the web alias of a local-only slug is drawn tinted toward its own colours; the official model and the plain 源石虫 are not', async () => {
|
||||
const store = (local, fail = () => false) => ({
|
||||
picture: () => null, image: async () => null,
|
||||
spineEntry: (id) => spineEntry(MANIFEST, id, local ? { local } : undefined),
|
||||
spine: { acquire: async (e) => { if (fail(e)) throw new Error('404'); return { animations: Object.keys(e.animations).map((name) => ({ name })) }; }, release() {} },
|
||||
});
|
||||
const local = localManifest(SLUGS);
|
||||
const mk = (id, st) => new UnitView(fakeViewCtx(fake.P, { assets: st, cam }), info(id));
|
||||
const web0 = mk(SLUGS[0], store(null)), web1 = mk(SLUGS[1], store(null));
|
||||
const official = mk(SLUGS[0], store(local)), failed = mk(SLUGS[1], store(local, (e) => e.local));
|
||||
const plain = mk('enemy_1007_slime', store(local));
|
||||
for (let i = 0; i < 4; i++) await tick();
|
||||
const all = [web0, web1, official, failed, plain];
|
||||
for (const v of all) v.update(1 / 60, cam(), 0);
|
||||
assert.equal(web0.actor.entry, webOf(SLUGS[0]), 'not extracted: the plain 源石虫 skeleton');
|
||||
assert.equal(web0.actor.spine.tint, ALIAS_TINT[SLUGS[0]], '灼热源石虫: orange');
|
||||
assert.equal(web1.actor.spine.tint, ALIAS_TINT[SLUGS[1]], '炽焰源石虫: red-orange');
|
||||
assert.notEqual(ALIAS_TINT[SLUGS[0]], ALIAS_TINT[SLUGS[1]], 'the two read apart');
|
||||
assert.ok(official.actor.entry.local);
|
||||
assert.equal(official.actor.spine.tint, 0xffffff, 'the official model as it is');
|
||||
assert.equal(failed.actor.entry, webOf(SLUGS[1]));
|
||||
assert.equal(failed.actor.spine.tint, ALIAS_TINT[SLUGS[1]], 'the web fallback of a failed official model is tinted too');
|
||||
assert.equal(plain.actor.spine.tint, 0xffffff, 'the plain 源石虫 itself');
|
||||
web0.flags = UF.FROZEN;
|
||||
web0.update(1 / 60, cam(), 1 / 60);
|
||||
assert.equal(web0.actor.spine.tint, 0x9fd4ff, 'a status tint wins (frozen)');
|
||||
for (const v of all) v.destroy?.();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user