SOURCE / PINNED RELEASE
Made of little things.
Sickman Survival
- Release
- 7335c9fdf0a8…
- Author-recorded commit
- cbd2eeafa70d…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-8804151df7f
Archive hash verified: 591bab4addcbe5fc…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import assert from 'node:assert/strict';
import test from 'node:test';
import { layoutPanes, Roster } from '../src/roster.ts';
const slots = (roster) => roster.players.map((p) => [p.slot, p.source]);
test('keyboard and a single pad share one solo player', () => {
const roster = new Roster();
assert.deepEqual(roster.sync([]), { added: [], removed: [] });
assert.deepEqual(roster.sync([0]), { added: [], removed: [] });
assert.equal(roster.mode, 'solo');
assert.deepEqual(slots(roster), [[0, 'solo']]);
});
test('a second pad splits the screen without any setup; P1 keeps slot 0', () => {
const roster = new Roster();
roster.sync([0]);
const change = roster.sync([0, 1]);
assert.equal(roster.mode, 'split');
assert.deepEqual(change, { added: [1], removed: [] });
assert.deepEqual(slots(roster), [[0, 0], [1, 1]]);
});
test('two pads at once, and pads with index gaps', () => {
const roster = new Roster();
assert.deepEqual(roster.sync([3, 1]).added, [1]);
assert.deepEqual(slots(roster), [[0, 1], [1, 3]]);
});
test('more pads fill to four players; a fifth waits for a free slot', () => {
const roster = new Roster();
roster.sync([0, 1, 2, 3, 4]);
assert.deepEqual(slots(roster), [[0, 0], [1, 1], [2, 2], [3, 3]]);
const change = roster.sync([0, 2, 3, 4]);
assert.deepEqual(change, { added: [1], removed: [1] });
assert.deepEqual(slots(roster), [[0, 0], [1, 4], [2, 2], [3, 3]], 'the waiting pad takes the freed slot');
});
test('unplugging removes that player; others keep their slots', () => {
const roster = new Roster();
roster.sync([0, 1, 2]);
assert.deepEqual(roster.sync([1, 2]), { added: [], removed: [0] });
assert.deepEqual(slots(roster), [[1, 1], [2, 2]]);
// A new pad takes the lowest free slot.
assert.deepEqual(roster.sync([1, 2, 5]).added, [0]);
});
test('back down to one pad returns to solo, with keyboard re-merged', () => {
const roster = new Roster();
roster.sync([0, 1]);
assert.deepEqual(roster.sync([1]), { added: [], removed: [0] });
assert.equal(roster.mode, 'solo');
assert.deepEqual(slots(roster), [[1, 'solo']]);
});
test('if every pad leaves at once, the first player stays for the keyboard', () => {
const roster = new Roster();
roster.sync([0, 1, 2]);
assert.deepEqual(roster.sync([]), { added: [], removed: [1, 2] });
assert.equal(roster.mode, 'solo');
assert.deepEqual(slots(roster), [[0, 'solo']]);
});
test('a different pad on the same index is a leave and a fresh join', () => {
const roster = new Roster();
roster.sync([0, 1, 2]);
assert.deepEqual(roster.sync([0, 1, 2], 'ready', [1]), { added: [1], removed: [1] });
assert.equal(roster.players.length, 3);
});
test('a blocked or missing gamepad API leaves the roster alone', () => {
const roster = new Roster();
roster.sync([0, 1]);
assert.deepEqual(roster.sync([], 'blocked'), { added: [], removed: [] });
assert.deepEqual(roster.sync([], 'unavailable'), { added: [], removed: [] });
assert.equal(roster.players.length, 2);
});
test('panes tile the frame exactly at odd sizes', () => {
for (const [w, h] of [[201, 161], [320, 560], [901, 599], [1920, 1080]]) {
for (let count = 1; count <= 4; count++) {
const rects = layoutPanes(count, w, h);
assert.ok(rects.length >= count);
let area = 0;
for (const r of rects) {
assert.ok(r.x >= 0 && r.y >= 0 && r.x + r.width <= w && r.y + r.height <= h);
area += r.width * r.height;
}
assert.equal(area, w * h, `${count} panes cover ${w}×${h}`);
}
}
});
test('two players split along the axis that keeps panes closest to the game view', () => {
const wide = layoutPanes(2, 1920, 600);
assert.equal(wide[1].x, 960, 'very wide frames split side by side');
const landscape = layoutPanes(2, 900, 600);
assert.equal(landscape[1].x, 450, 'landscape frames split side by side');
const square = layoutPanes(2, 600, 600);
assert.equal(square[1].y, 300, 'square frames stack');
const tall = layoutPanes(2, 320, 560);
assert.equal(tall[1].y, 280, 'portrait frames stack');
assert.equal(layoutPanes(3, 900, 600).length, 4, 'three players use a 2×2 grid with a spare cell');
});
