The tour rides the wire: your friend plays in the body they picked

A tour is which body you play in, so it belongs in the look message next to the
kit, the skin and the name. Without it both phones drew the friend in whatever
tour THIS phone had picked, and a men's/women's pairing showed each player
someone their friend was not using.

`oTour()` is the one accessor — yours everywhere, theirs in netplay — and it
feeds the three things that must agree about a body: the sprite suffix, how the
art takes a colour (her whites wash, his blue dyes) and the palm the idle ball
bounces under. An older build sends no tour and falls back to ours, which is
what it did before.

The palm knobs were re-checked against the new front sheets rather than assumed
— the poses were copied, so the ball still lands under the drawn hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-20 16:11:13 +02:00
1 parent 5f9170b7d7
commit c5eb6846e3
5 files changed
+44 -11

No files matched your search

+10 -3
View File
@@ -17,8 +17,8 @@
//
// ponytail: no reconnect-and-resume. A dropped radio ends the match with a card; the recorded
// plan calls reconnection "the bulk of the real work" and a prototype must not start there.
import { COURT, UI, KITS, SKINS } from './config.js';
import { G, P, O, ball, score, setupPoint, selectCourt, court, kit, skin } from './state.js';
import { COURT, UI, KITS, SKINS, TOURS } from './config.js';
import { G, P, O, ball, score, setupPoint, selectCourt, court, kit, skin, tour } from './state.js';
import { pointTo, fault, tossBall } from './gameplay.js';
import { startMatch, matchReady, resolveInterlude, netMatchWon } from './beats.js'; // runtime-only cycle: beats sends verdicts back through this module
import { zombies } from './zombies.js';
@@ -129,7 +129,11 @@ function netStart(side, death, courtId) {
// tell them how we look AND WHO WE ARE, the moment there is somebody to tell. Sent once, on
// pairing: neither phone can change its pick mid-match, because the setup screen is only
// reachable between runs.
send({ t: 'look', k: kit.id, s: skin.id, n: savedName() });
// `u` is the TOUR — which body they play in, not just what colour it wears. It rides the same
// message because it is the same fact: what the friend across the net LOOKS like. Without it both
// phones drew the opponent in whatever tour THIS phone picked, so a men's/women's pairing showed
// each player a body their friend was not using.
send({ t: 'look', k: kit.id, s: skin.id, n: savedName(), u: tour.id });
G.modal = null; G.modalQ.length = 0; // the waiting card has done its job
// THE HOST'S COURT RIDES THE ROOM, exactly as the death-match pick does — two phones playing the
// same rally on two different surfaces disagree about the bounce as well as the picture, and the
@@ -202,6 +206,9 @@ export function netMsg(m) {
else if (m.t === 'look') G.net.look = {
kit: KITS.find(k => k.id === m.k) || KITS[0],
skin: SKINS.find(s => s.id === m.s) || SKINS[0],
// a build that predates the tour field sends none, and the fallback is what it used to do:
// draw them in OUR tour. Same rule as the kit — an unknown id resolves to the drawn default.
tour: TOURS.find(t => t.id === m.u) || tour,
name: tidyName(m.n || '').slice(0, 8) || 'FRIEND',
};
}