The whites take a wash, the friend brings their own colours, and the menu shows the man

Three things off the back of the picker.

THE LIVE PLAYER on the setup screen is the real sprite through the real tint, not
a mock-up of the pick — so the menu cannot promise a colour the court will not
keep. It redraws on every tap of a tour, a kit or a skin.

THE WOMEN'S TOUR IS NO LONGER SAT OUT. Her set is drawn head to toe in whites, so
there was no hue to rotate — the answer is not a dye but a WASH: the pick laid
over her whites at a low saturation with the lightness kept, which is how you dye
a white kit in life and gives rose, powder blue, mint, lilac. Her hair and skin
fall out of it on lightness alone, which is exactly what a dye could not manage.

THE FRIEND ACROSS THE NET now wears what they picked on their own phone. Their
look crosses on pairing, and since the opponent art is red rather than blue the
kit rides its own hue arc — safe there for the one reason it would not be on the
player's own sheets: the opponent sprites carry no drawn blood. An older build
simply never sends it and the opponent draws as before; the match still plays.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-15 15:23:34 +02:00
1 parent 1daa3e9d6a
commit b5838b2b24
6 files changed
+194 -64

No files matched your search

+64 -9
View File
@@ -1579,15 +1579,70 @@ test('BLOOD STAYS BLOOD — no skin pick may turn the death frames to mud', () =
assert.equal(tintPx(...POOL, k, cfg.SKINS[0]), null, `${k.id} leaves the pool red`);
});
// A tour whose art has no kit sits the kit pick out — otherwise the only thing on her sheets inside
// the kit's hue arc is HER HAIR, and picking ACID dyed it green.
test("a tour played in whites carries no kit, so nothing of hers is dyed", () => {
const women = cfg.TOURS.find(t => t.id === 'women');
assert.equal(women.kit, false, 'the women\'s set is flagged as having no kit');
assert.equal(cfg.TOURS.find(t => t.id === 'men').kit, true, 'the men\'s set has one');
const HER_HAIR = [40, 44, 72]; // dark blue-black, hue ~220 — inside the kit arc
assert.ok(tintPx(...HER_HAIR, cfg.KITS[1], cfg.SKINS[0]), 'her hair IS in the kit arc (the trap)');
assert.equal(tintPx(...HER_HAIR, null, cfg.SKINS[0]), null, 'and a null kit is what spares it');
// A WHITE KIT IS WASHED, NOT DYED. Rotating a near-neutral does nothing, and the only saturated
// blue on her sheets is HER HAIR — same hue as his shorts, so a dye turned it green. Colours
// measured off p_idle1_f.
const HER_SKIRT = [208, 216, 224], HER_BRIGHT = [240, 248, 248]; // cool-cast whites
const HER_HAIR = [40, 44, 72]; // dark blue-black, hue ~220
test('a white kit takes a wash: her whites colour, her hair and skin do not', () => {
assert.equal(cfg.TOURS.find(t => t.id === 'women').kit, 'wash');
assert.equal(cfg.TOURS.find(t => t.id === 'men').kit, 'dye');
const red = cfg.KITS.find(k => k.id === 'red');
for (const c of [HER_SKIRT, HER_BRIGHT]) {
const out = tintPx(...c, red, cfg.SKINS[0], 'wash');
assert.ok(out, 'her whites take the colour');
assert.equal(dom(out), 'r', 'CLARET reads as rose on her');
// a WASH, not a dye: it must stay pale, or she is wearing a block of poster paint
assert.ok(Math.min(...out) > 150, `${c} stays a pastel, not a solid (${out})`);
}
// the trap this whole mode exists to avoid, pinned from both sides
assert.ok(tintPx(...HER_HAIR, red, cfg.SKINS[0], 'dye'), 'her hair IS in the dye arc — the trap');
assert.equal(tintPx(...HER_HAIR, red, cfg.SKINS[0], 'wash'), null, 'a wash cannot reach it');
assert.equal(tintPx(...SKINPX, red, cfg.SKINS[0], 'wash'), null, 'nor her skin');
});
test('the wash leaves the shading: her folds stay in order', () => {
const red = cfg.KITS.find(k => k.id === 'red');
const lum = c => c[0] * 0.3 + c[1] * 0.59 + c[2] * 0.11;
const lit = tintPx(...HER_BRIGHT, red, cfg.SKINS[0], 'wash');
const shade = tintPx(...HER_SKIRT, red, cfg.SKINS[0], 'wash');
assert.ok(lum(lit) > lum(shade), 'the lit fold stays lighter than the shaded one');
});
// NETPLAY: the friend across the net wears their own pick. Their sprites are RED, not blue, so the
// kit rides its own arc — safe there only because the opponent art carries no drawn blood.
test("the opponent's red kit takes a dye on its own arc, and his skin still reads as skin", () => {
const HIS_SHIRT = [184, 8, 8], HER_TOP = [184, 48, 48], HIS_SKIN = [248, 176, 128];
const green = cfg.KITS.find(k => k.id === 'green');
for (const [nm, c] of [['his', HIS_SHIRT], ['hers', HER_TOP]]) {
const out = tintPx(...c, green, cfg.SKINS[0], 'dye', cfg.TINT.oKit);
assert.ok(out && dom(out) === 'g', `${nm} red shirt takes the pick`);
}
// his skin must not go with it, and the player's own blue arc must not claim his shirt
assert.equal(tintPx(...HIS_SKIN, green, cfg.SKINS[0], 'dye', cfg.TINT.oKit), null,
'the opponent kit arc leaves his skin alone');
assert.equal(tintPx(...HIS_SHIRT, green, cfg.SKINS[0], 'dye', cfg.TINT.kit), null,
"and the PLAYER's blue arc never claims the opponent's red");
});
// The look crosses the wire ONCE, on pairing — neither phone can change its pick mid-match, because
// the setup screen is only reachable between runs.
test('netplay: your look goes over the wire on pairing, and theirs comes back', () => {
state.selectKit('green'); state.selectSkin('deep');
freshNet('A');
const look = netTest.sent.find(m => m.t === 'look');
assert.ok(look, 'pairing announces this phone\'s look');
assert.equal(look.k, 'green'); assert.equal(look.s, 'deep');
netMsg({ t: 'look', k: 'red', s: 'pale' });
assert.equal(G.net.look.kit.id, 'red', 'their kit is what the opponent is drawn in');
assert.equal(G.net.look.skin.id, 'pale');
// an older build, or a colour this one has never heard of, must not blank the opponent out
netMsg({ t: 'look', k: 'chartreuse', s: undefined });
assert.equal(G.net.look.kit.id, cfg.KITS[0].id, 'an unknown colour falls back to as-drawn');
assert.equal(G.net.look.skin.id, cfg.SKINS[0].id);
state.selectKit(cfg.KITS[0].id); state.selectSkin(cfg.SKINS[0].id); // leave the table as found
});
test('every kit and skin in the tables is a live, distinct pick', () => {