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
+42
-2
@@ -3,8 +3,8 @@
|
||||
// Module map: config (tuning/constants) · state (mutable game state) · gameplay (logic/update)
|
||||
// render (camera/drawing) · input (keys/touch) · audio (sfx/umpire) · panel (T tuning UI)
|
||||
|
||||
import { TUNE, COURTS, TOURS, MODES, MENU, DIFFS, EATEN_RESTART, DONE_RESTART, WEAPONS, bossArt, CUP_ROUNDS, matchFoe } from './config.js';
|
||||
import { P, O, ball, score, G, setupPoint, selectCourt, selectTour } from './state.js';
|
||||
import { TUNE, COURTS, TOURS, KITS, SKINS, MODES, MENU, DIFFS, EATEN_RESTART, DONE_RESTART, WEAPONS, bossArt, CUP_ROUNDS, matchFoe } from './config.js';
|
||||
import { P, O, ball, score, G, setupPoint, selectCourt, selectTour, selectKit, selectSkin } from './state.js';
|
||||
import { update, restartRun, keyPressed, solveShot, inServeBox, inCourt, forceZombies, startGore, splatPoint, deliverTin, selectWeapon, grantWeapon, showModal, dismissModal, flickTarget, dragServe, toggleTestMode } from './gameplay.js';
|
||||
import { startDuel, resolveInterlude, startBossFight, endBossFight, startDrill, endDrill, startMatch, endMatch, startBench, endBench, FALL, benchFall, startNetVs } from './beats.js';
|
||||
import { netConnect, netHooks } from './net.js';
|
||||
@@ -186,10 +186,49 @@ for (const t of TOURS) {
|
||||
b.addEventListener('click', () => {
|
||||
pickedTour = t.id;
|
||||
for (const el of tourList.children) el.classList.toggle('on', el === b);
|
||||
kitRowFor(t);
|
||||
summary();
|
||||
});
|
||||
tourList.appendChild(b);
|
||||
}
|
||||
// A TOUR PLAYED IN WHITES HAS NO KIT TO DYE (see TOURS.kit), and a row of swatches that silently
|
||||
// does nothing is worse than no row — you tap all six and conclude the feature is broken. The row
|
||||
// goes dead and the header says why, in the same slot that names the colour when it is live.
|
||||
function kitRowFor(t) {
|
||||
const off = t.kit === false;
|
||||
document.getElementById('kitRow').classList.toggle('dead', off);
|
||||
if (off) document.getElementById('kitName').textContent = 'PLAYED IN WHITES';
|
||||
else document.getElementById('kitName').textContent =
|
||||
(KITS.find(e => e.id === pickedKit) || KITS[0]).name;
|
||||
}
|
||||
|
||||
// --- KIT and SKIN swatches, built from the tables. Same rule as the tour tiles: tapping selects and
|
||||
// nothing more, SERVE UP is what commits. Both rows are one builder — they differ only in which
|
||||
// table they read and which localStorage key they remember, and a second copy of this loop to say
|
||||
// "skin" instead of "kit" would be the same eleven lines twice.
|
||||
const swatches = (rowId, nameId, table, keyName, get, set) => {
|
||||
const row = document.getElementById(rowId), label = document.getElementById(nameId);
|
||||
for (const e of table) {
|
||||
const b = document.createElement('button');
|
||||
b.className = e.id === get() ? 'on' : '';
|
||||
b.style.background = e.sw;
|
||||
b.title = e.name; // desktop hover; the h3 label is what a phone reads
|
||||
b.setAttribute('aria-label', e.name);
|
||||
b.addEventListener('click', () => {
|
||||
set(e.id);
|
||||
try { localStorage.setItem(keyName, e.id); } catch {}
|
||||
for (const el of row.children) el.classList.toggle('on', el === b);
|
||||
label.textContent = e.name;
|
||||
});
|
||||
row.appendChild(b);
|
||||
}
|
||||
label.textContent = (table.find(e => e.id === get()) || table[0]).name;
|
||||
};
|
||||
let pickedKit = remembered('kit') || KITS[0].id;
|
||||
let pickedSkin = remembered('skin') || SKINS[0].id;
|
||||
swatches('kitRow', 'kitName', KITS, 'kit', () => pickedKit, id => pickedKit = id);
|
||||
swatches('skinRow', 'skinName', SKINS, 'skin', () => pickedSkin, id => pickedSkin = id);
|
||||
kitRowFor(TOURS.find(t => t.id === pickedTour) || TOURS[0]); // a remembered women's tour comes up dead too
|
||||
|
||||
// --- court rows, built from the table.
|
||||
// The two meters come out of the court's OWN bounce numbers rather than a pair of hand-written
|
||||
@@ -323,6 +362,7 @@ function refreshSplash() {
|
||||
function start(modeId = pickedMode) {
|
||||
if (!menuUp()) return;
|
||||
selectTour(pickedTour);
|
||||
selectKit(pickedKit); selectSkin(pickedSkin);
|
||||
selectCourt(picked);
|
||||
menuDuck(true); // the theme belongs to the menus — hand the court over to the ambience
|
||||
intro.hidden = true; ambient(); voice('quiet_please'); // low atmosphere bed starts with the first gesture
|
||||
|
||||
Reference in new issue
Block a user