You pick your own kit and your own skin, and the blood stays red
Testers asked for some control over how their player looks. From behind — the only way you ever see him — there are two things worth controlling, and neither needs a single new asset: the kit and the skin are one hue-bucket pixel pass over the sheets already loaded, cached per frame and built on first draw. The buckets are measured, not guessed, and three neighbours had to be fenced off one at a time: the shirt's warm HIGHLIGHTS (which spattered the man with mud on every dark pick), the drawn BLOOD on the death frames (which turned the pool brown — the worst bug this file could ship), and the women's HAIR, which is the same hue as his shorts and cannot be told apart from it by any rule. Her set is played in whites and has no kit to dye, so it is flagged out and the row says so rather than silently doing nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2f0c04fad4
commit
3b639e743b
6 files changed
+321
-11
No files matched your search
+66
-2
@@ -747,11 +747,75 @@ export const CUP_ROUNDS = [
|
||||
];
|
||||
|
||||
export const TOURS = [
|
||||
{ id: 'men', name: "MEN'S SINGLES", tile: 'tour_m', sfx: null },
|
||||
{ id: 'women', name: "WOMEN'S SINGLES", tile: 'tour_w', sfx: '_f', dx: 0.46, apex: 0.78, tossDx: -0.59,
|
||||
// `kit` — whether this tour's art HAS a kit to dye. The men's set is drawn in blue shorts, so it
|
||||
// does. The women's set is drawn in WHITES: her skirt is (240,248,248)/(208,216,224), a cool-cast
|
||||
// white, and the only genuinely saturated blue anywhere on her sheets is HER HAIR — same hue as
|
||||
// his shorts (210-240) and overlapping in lightness, so no hue or lightness rule can tell the two
|
||||
// apart. A kit tint on her set therefore dyes her hair and nothing else, which is why she is
|
||||
// flagged out of it rather than fenced around. THE FIX IS ART, NOT CODE: give her set a coloured
|
||||
// kit through the sprite pipeline and this becomes `kit: true` with no other change.
|
||||
{ id: 'men', name: "MEN'S SINGLES", tile: 'tour_m', sfx: null, kit: true },
|
||||
{ id: 'women', name: "WOMEN'S SINGLES", tile: 'tour_w', sfx: '_f', kit: false, dx: 0.46, apex: 0.78, tossDx: -0.59,
|
||||
oDx: 0.48, oApex: 0.73 },
|
||||
];
|
||||
|
||||
// THE PLAYER'S OWN COLOURS — testers asked for "some control over how my player looks", and from
|
||||
// behind (which is the only way you ever see him) there are exactly two things to control: the kit
|
||||
// and the skin. No new art: both are a PIXEL PASS over the sheets already loaded, keyed by hue —
|
||||
// see tint() in render.js. Measured on p_idle1.png (84x224, 1510 opaque colours), the sheets fall
|
||||
// into three clean hue populations: the kit blue around 208 deg, the skin around 22 deg, and the
|
||||
// near-neutral shirt whites at sat < 0.18 that neither bucket touches.
|
||||
//
|
||||
// `h` is the ABSOLUTE hue the bucket is rotated onto; `s`/`l` scale saturation and lightness from
|
||||
// whatever the pixel already had, so every bit of the drawn shading survives the swap. An entry
|
||||
// with no `h` is "as drawn" and costs nothing — the tint pass is skipped entirely.
|
||||
//
|
||||
// The kit is the whole kit, not the shorts: the run frames carry blue down at the shoes and the
|
||||
// women's set is blue well above the waist, so a hue swap moves every blue on the body together.
|
||||
// That is the feature — you pick a KIT — and the alternative is a per-region mask baked by the
|
||||
// sprite pipeline, which is a lot of work for a distinction nobody asked for.
|
||||
export const KITS = [
|
||||
{ id: 'blue', name: 'CLUB BLUE', sw: '#1678ba' }, // as drawn
|
||||
{ id: 'red', name: 'CLARET', sw: '#b83030', h: 356, s: 0.92 },
|
||||
{ id: 'green', name: 'ACID', sw: '#6ea52a', h: 88, s: 0.86 },
|
||||
{ id: 'purple', name: 'VIOLET', sw: '#7a46c0', h: 268 },
|
||||
{ id: 'orange', name: 'CLAY', sw: '#d4761e', h: 28, s: 0.94 },
|
||||
{ id: 'black', name: 'BLACKOUT', sw: '#2e3138', h: 220, s: 0.16, l: 0.42 },
|
||||
];
|
||||
// Skin is NOT a hue rotation — rotate a skin tone and you get a green man. Real tones sit in a
|
||||
// narrow hue band and differ in lightness and saturation, so these mostly move `l`.
|
||||
export const SKINS = [
|
||||
{ id: 'tan', name: 'TAN', sw: '#d6956f' }, // as drawn
|
||||
{ id: 'pale', name: 'PALE', sw: '#eec4a4', h: 26, s: 0.72, l: 1.14 },
|
||||
{ id: 'olive', name: 'OLIVE', sw: '#b8825a', h: 27, s: 0.94, l: 0.9 },
|
||||
{ id: 'brown', name: 'BROWN', sw: '#8e5a38', h: 22, s: 1.02, l: 0.68 },
|
||||
{ id: 'deep', name: 'DEEP', sw: '#5d3722', h: 20, s: 1.05, l: 0.46 },
|
||||
];
|
||||
// The hue windows the two buckets claim, in degrees, and the saturation each needs before it will
|
||||
// claim a pixel at all. The kit window runs generously wide (150-300) because the ~165 anti-aliased
|
||||
// pixels where blue meets the white shirt land out at 270-300, and a tight window leaves them
|
||||
// behind as a dull fringe when the kit moves far. Nothing else on the sheet is blue, so its floor
|
||||
// can stay low and catch every soft edge.
|
||||
//
|
||||
// THE SKIN BUCKET IS FENCED ON THREE SIDES, AND EVERY NUMBER IS MEASURED. It has two neighbours it
|
||||
// must not touch, and each needed a different fence:
|
||||
//
|
||||
// THE SHIRT, above it. The whites are not neutral — they are WARM, hue ~37, running up to
|
||||
// (243,236,224) on the run and smash frames — so they sit inside the skin's hue arc. Tinted, they
|
||||
// came out as mud spattered across the man's back on every dark pick. Saturation does not
|
||||
// separate them (they reach 0.35, inside real skin's range); LIGHTNESS does. Real leg skin never
|
||||
// exceeds lum 0.77 across p_idle1/p_run1/p_smash/p_idle1_f, and the shirt whites start at 0.82,
|
||||
// so `lumSkin` sits in that gap.
|
||||
//
|
||||
// THE BLOOD, beside it — and this is the one that matters, because this is Splatter Tennis. The
|
||||
// death frames carry DRAWN blood (spray on the shirt, a pool under the body), and at a 330 deg
|
||||
// lower edge a DEEP pick turned every drop of it to mud. Saturation cannot separate blood either:
|
||||
// blood runs 0.8-0.9 and so does real skin ((240,160,128) is a highlight at 0.79). HUE separates
|
||||
// them cleanly and only just — blood is a spike at 0 deg, skin peaks at 16-24 — so the arc starts
|
||||
// at 10. What that leaves untinted on the death frames is the reddest skin, which is the
|
||||
// blood-soaked skin, and it reads as gore rather than as a miss.
|
||||
export const TINT = { kit: [150, 300], skin: [10, 45], satKit: 0.15, satSkin: 0.28, lumSkin: 0.80 };
|
||||
|
||||
// netW was the singles-stick width (10.06) while the court is drawn to the DOUBLES lines, so the
|
||||
// net stopped 0.46 m short of each sideline and visibly failed to meet the court edge. The posts
|
||||
// now stand exactly on the doubles sidelines.
|
||||
|
||||
Reference in new issue
Block a user