@bongos/core 1.24.94 → 1.24.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +107 -27
- package/docs/adr/0405-each-character-has-one-colour-and-the-visor-wears-it.md +31 -0
- package/docs/adr/README.md +1 -0
- package/docs/copy-inventory.md +7 -7
- package/docs/copy-registry.json +7 -7
- package/docs/design/characters/v2/artisan-visor.png +0 -0
- package/docs/design/characters/v2/artisan.png +0 -0
- package/docs/design/characters/v2/artist-visor.png +0 -0
- package/docs/design/characters/v2/artist.png +0 -0
- package/docs/design/characters/v2/draw-characters.js +465 -0
- package/docs/design/characters/v2/patron-visor.png +0 -0
- package/docs/design/characters/v2/patron.png +0 -0
- package/docs/design/characters/v2/sheet.html +114 -0
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-readings.json +35 -35
- package/modules/hall-ui/public/characters/artisan-face.png +0 -0
- package/modules/hall-ui/public/characters/artisan-visor.png +0 -0
- package/modules/hall-ui/public/characters/artisan.png +0 -0
- package/modules/hall-ui/public/characters/artist-face.png +0 -0
- package/modules/hall-ui/public/characters/artist-visor.png +0 -0
- package/modules/hall-ui/public/characters/artist.png +0 -0
- package/modules/hall-ui/public/characters/patron-face.png +0 -0
- package/modules/hall-ui/public/characters/patron-visor.png +0 -0
- package/modules/hall-ui/public/characters/patron.png +0 -0
- package/modules/hall-ui/public/craft-mode.js +15 -4
- package/modules/hall-ui/public/style.css +18 -5
- package/modules/hall-ui/public/visor.css +20 -5
- package/modules/hall-ui/public/visor.js +6 -3
- package/modules/hall-ui/records/characters.md +19 -0
- package/modules/hall-ui/records/visor.md +3 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/module-api.js +1 -1
- package/tests/character_creation.mjs +44 -7
- package/tests/hall_visor.mjs +28 -2
|
@@ -153,7 +153,7 @@ test('a held character is the only one offered, and the top bar wears its sprite
|
|
|
153
153
|
const sw = t.C.switchHtml();
|
|
154
154
|
assert.match(sw, /craft-switch--character/);
|
|
155
155
|
assert.match(sw, /data-craft-me/);
|
|
156
|
-
assert.match(sw, /\/builders\/characters\/artisan\.png
|
|
156
|
+
assert.match(sw, /\/builders\/characters\/artisan-face\.png/, 'the chip is the head, its own picture (task 1005218)');
|
|
157
157
|
assert.ok(!/data-craft-opt=/.test(sw), 'the three-way switch is gone');
|
|
158
158
|
// A session's stored mode from before the character cannot override it.
|
|
159
159
|
t.session['hall-craft'] = 'patron';
|
|
@@ -162,7 +162,7 @@ test('a held character is the only one offered, and the top bar wears its sprite
|
|
|
162
162
|
|
|
163
163
|
test('the sprite is drawn on first paint from the last held character, before /me answers', () => {
|
|
164
164
|
const t = boot({ store: { 'hall-character': 'patron' } });
|
|
165
|
-
assert.match(t.C.switchHtml(), /\/builders\/characters\/patron\.png/);
|
|
165
|
+
assert.match(t.C.switchHtml(), /\/builders\/characters\/patron-face\.png/);
|
|
166
166
|
});
|
|
167
167
|
|
|
168
168
|
test('an older core that says nothing about characters changes nothing', () => {
|
|
@@ -187,15 +187,52 @@ test('the character section never writes `hidden` onto the nav: a character only
|
|
|
187
187
|
assert.ok(!/nav-item|app-sidebar/.test(section), 'the character code does not touch the nav itself; applyNav does');
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
-
|
|
190
|
+
// task 1005218: the second look. Three pictures per character, drawn by
|
|
191
|
+
// docs/design/characters/v2/draw-characters.js --hall: the whole figure (64), the head for
|
|
192
|
+
// the chip (32) and the head wearing the visor for the visor's corner (32).
|
|
193
|
+
test('each character\'s three pictures are on disk, at their sizes', () => {
|
|
191
194
|
for (const id of ['artisan', 'artist', 'patron']) {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
for (const [part, size] of [['', 64], ['-face', 32], ['-visor', 32]]) {
|
|
196
|
+
const f = path.join(HALL, 'characters', id + part + '.png');
|
|
197
|
+
assert.ok(fs.existsSync(f), `${id}${part}.png`);
|
|
198
|
+
const buf = fs.readFileSync(f);
|
|
199
|
+
assert.deepEqual([...buf.subarray(0, 8)], [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], `${id}${part}.png is a PNG`);
|
|
200
|
+
assert.deepEqual([buf.readUInt32BE(16), buf.readUInt32BE(20)], [size, size], `${id}${part}.png is ${size} by ${size}`);
|
|
201
|
+
}
|
|
196
202
|
}
|
|
197
203
|
});
|
|
198
204
|
|
|
205
|
+
test('the hall\'s pictures are what the drawing script draws: nobody edited one by hand', async () => {
|
|
206
|
+
const { createRequire } = await import('node:module');
|
|
207
|
+
const draw = createRequire(import.meta.url)(path.join(ROOT, 'docs/design/characters/v2/draw-characters.js'));
|
|
208
|
+
const drawn = draw.hallFiles();
|
|
209
|
+
const onDisk = fs.readdirSync(path.join(HALL, 'characters')).sort();
|
|
210
|
+
assert.deepEqual(onDisk, Object.keys(drawn).sort(), 'nine files, three per character, and nothing else');
|
|
211
|
+
// Compared pixel for pixel, not byte for byte: two zlib builds may pack the same picture differently.
|
|
212
|
+
const zlib = await import('node:zlib');
|
|
213
|
+
const pixels = (png) => {
|
|
214
|
+
const parts = [];
|
|
215
|
+
for (let at = 8; at < png.length;) {
|
|
216
|
+
const len = png.readUInt32BE(at), type = png.toString('latin1', at + 4, at + 8);
|
|
217
|
+
if (type === 'IDAT') parts.push(png.subarray(at + 8, at + 8 + len));
|
|
218
|
+
at += 12 + len;
|
|
219
|
+
}
|
|
220
|
+
return zlib.inflateSync(Buffer.concat(parts));
|
|
221
|
+
};
|
|
222
|
+
for (const [name, buf] of Object.entries(drawn)) {
|
|
223
|
+
assert.ok(pixels(buf).equals(pixels(fs.readFileSync(path.join(HALL, 'characters', name)))), name + ' is stale or hand-edited: run node docs/design/characters/v2/draw-characters.js --hall');
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('the three characters\' colours are mixed from the hall\'s tokens, never typed in (ADR 0405)', () => {
|
|
228
|
+
const css = fs.readFileSync(path.join(HALL, 'style.css'), 'utf8');
|
|
229
|
+
const block = css.slice(css.indexOf('--char-artisan:'), css.indexOf('--char-patron:') + 120);
|
|
230
|
+
assert.match(block, /--char-artisan: var\(--accent\);/);
|
|
231
|
+
assert.match(block, /--char-artist: color-mix\(/);
|
|
232
|
+
assert.match(block, /--char-patron: color-mix\(/);
|
|
233
|
+
assert.ok(!/#[0-9a-fA-F]{3,8}/.test(block), 'no literal colour');
|
|
234
|
+
});
|
|
235
|
+
|
|
199
236
|
// ---------- the server half ----------
|
|
200
237
|
test('core_297 adds the account column, and the CHECK names exactly the three characters', () => {
|
|
201
238
|
const sql = fs.readFileSync(path.join(ROOT, 'migrations/core_297_builder_character.sql'), 'utf8');
|
package/tests/hall_visor.mjs
CHANGED
|
@@ -179,7 +179,7 @@ const SCENE = `<script>
|
|
|
179
179
|
cv.addEventListener('pointerup', () => { drag = null; });
|
|
180
180
|
addEventListener('keydown', (e) => { if (e.key === 'Escape' && window.picked) { e.preventDefault(); window.picked = false; } });
|
|
181
181
|
window.OTBCraftMode = { held: 'artisan', character() { return this.held; }, current() { return null; },
|
|
182
|
-
me() { return { builder: { total_credits: 56021 } }; }, spriteSrc(id) { return '/builders/characters/' + id + '.png'; }, openTool(id) { window.toolOpened = id; } };
|
|
182
|
+
me() { return { builder: { total_credits: 56021 } }; }, spriteSrc(id) { return '/builders/characters/' + id + '.png'; }, portraitSrc(id) { return '/builders/characters/' + id + '-visor.png'; }, openTool(id) { window.toolOpened = id; } };
|
|
183
183
|
window.__visorScene = { name: 'test', place: 'Test city', actions: [{ label: 'Back to centre', key: '0', run() { window.centred = true; } }], keys: [{ label: 'Look around', key: 'drag' }] };
|
|
184
184
|
</script>`;
|
|
185
185
|
// `helm` links the hall's real sheet (the token tier) instead of the stand-in colours.
|
|
@@ -240,7 +240,7 @@ test('in game mode the glass shows the place, the main action, the key hints, th
|
|
|
240
240
|
// task 1005195: every other control is a quiet key hint. Actions kept its key and its sheet; nothing was dropped.
|
|
241
241
|
assert.deepEqual(g.hints, ['TYour thinking', 'AActions', 'EscOptions']);
|
|
242
242
|
assert.equal(g.arcs, 2, 'two arcs frame the glass');
|
|
243
|
-
assert.equal(g.sprite, '/builders/characters/artisan.png');
|
|
243
|
+
assert.equal(g.sprite, '/builders/characters/artisan-visor.png', 'your picture on the glass is you wearing the visor (task 1005218)');
|
|
244
244
|
assert.deepEqual(errors, []);
|
|
245
245
|
await ctx.close();
|
|
246
246
|
});
|
|
@@ -366,6 +366,32 @@ for (const [width, height] of [[1440, 900], [390, 844], [320, 640]]) {
|
|
|
366
366
|
});
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
// task 1005218 (ADR 0405): each character has one colour, and the glass wears the held one.
|
|
370
|
+
test('the glass wears the held character\'s colour: the Artisan\'s is the accent, the Artist\'s and the Patron\'s are their own', { skip }, async () => {
|
|
371
|
+
const { ctx, page } = await open({ helm: true });
|
|
372
|
+
const g = await page.evaluate(() => {
|
|
373
|
+
const probe = (v) => { const d = document.createElement('i'); d.style.color = v; document.body.appendChild(d); const c = getComputedStyle(d).color; d.remove(); return c; };
|
|
374
|
+
const out = { accent: probe('var(--accent)') };
|
|
375
|
+
for (const id of ['artisan', 'artist', 'patron']) {
|
|
376
|
+
window.OTBCraftMode.held = id; window.OTBVisor.render();
|
|
377
|
+
const cs = (s) => getComputedStyle(document.querySelector(s));
|
|
378
|
+
out[id] = { char: probe('var(--char-' + id + ')'), main: cs('.visor__main').backgroundColor, name: cs('.visor__name').color,
|
|
379
|
+
line: cs('.visor__line:not(.visor__line--under)').stroke, rim: cs('.visor__sprite').borderTopColor, src: document.querySelector('.visor__sprite').getAttribute('src') };
|
|
380
|
+
}
|
|
381
|
+
return out;
|
|
382
|
+
});
|
|
383
|
+
assert.equal(g.artisan.main, g.accent, 'an Artisan\'s glass is the accent it always was');
|
|
384
|
+
for (const id of ['artisan', 'artist', 'patron']) {
|
|
385
|
+
assert.equal(g[id].main, g[id].char, id + ': the one button is the character\'s colour');
|
|
386
|
+
assert.equal(g[id].name, g[id].char, id + ': so is the name');
|
|
387
|
+
assert.equal(g[id].line, g[id].char, id + ': and the frame\'s lines');
|
|
388
|
+
assert.equal(g[id].rim, g[id].char, id + ': and the rim of the picture');
|
|
389
|
+
assert.equal(g[id].src, '/builders/characters/' + id + '-visor.png');
|
|
390
|
+
}
|
|
391
|
+
assert.equal(new Set([g.artisan.char, g.artist.char, g.patron.char]).size, 3, 'three characters, three colours');
|
|
392
|
+
await ctx.close();
|
|
393
|
+
});
|
|
394
|
+
|
|
369
395
|
test('the glass is the kit\'s: the cut accent button, ice hints with no box, the name voice, the inset focus ring', { skip }, async () => {
|
|
370
396
|
const { ctx, page } = await open({ helm: true, width: 1440, height: 900 });
|
|
371
397
|
const g = await page.evaluate(() => {
|