A poster answers, a border promises: the question screens drop their checkbox half
Oliver named the wizard's wrongness precisely: a pre-highlighted card that also navigates is "a checkbox and a button combined in one". The border said "this is a setting", the tap said "this was an answer", and the screen left before the setting-reading of it could cash out. So the two roles live on different screens now. The question screens (PICK A GAME, DIFFICULTY, the zombie pair) carry NO selection: every poster and chip is full-bright and equal — the dimming went too, it read as disabled — and tapping answers and advances. The remembered run survives only as a small LAST tag in the card's corner: an indicator, not a selection. The final screen keeps its acid borders honestly, because SERVE UP is a real confirm — the rule is "a border means SERVE UP will use this; a bright poster means tap me to answer". Question screens rebuild on entry so LAST follows what was actually played, and the first card clears the header by the margin the old h3 rows used to give (Oliver's padding note). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
65be60b139
commit
6b795f77cc
2 files changed
+47
-33
No files matched your search
+33
-23
@@ -82,17 +82,22 @@ const modeOf = id => MODES.find(x => x.id === id);
|
||||
const topOf = m => MENU.find(t => t.mode === m || (t.kids && t.kids.includes(m))) || MENU[0];
|
||||
const modeList = document.getElementById('modeList');
|
||||
const modeSub = document.getElementById('modeSub');
|
||||
const banner = (card, cls, on) => {
|
||||
// QUESTION SCREENS CARRY NO SELECTION (Oliver: a highlighted card that also navigates is "a
|
||||
// checkbox and a button combined"). Every poster is full-bright and equal — all of them are live
|
||||
// answers — and the only memory shown is the LAST tag, an indicator rather than a selection. The
|
||||
// select-and-confirm border lives solely on the final screen, where SERVE UP actually confirms.
|
||||
const banner = (card, cls, last) => {
|
||||
const b = document.createElement('button');
|
||||
b.className = cls + (on ? ' on' : '');
|
||||
b.className = cls;
|
||||
// the scrim rides IN the background stack (gradient over art), so the caption needs no extra box
|
||||
b.style.backgroundImage =
|
||||
`linear-gradient(180deg, rgba(0,0,0,0) 34%, rgba(0,0,0,0.78)), url(assets/${card.art}.png?v=${SPRV})`;
|
||||
b.innerHTML = `<span class="cb"><b>${card.name}</b><i>${card.sub}</i></span>`;
|
||||
b.innerHTML = `<span class="cb"><b>${card.name}</b><i>${card.sub}</i></span>`
|
||||
+ (last ? '<u class="last">LAST</u>' : '');
|
||||
return b;
|
||||
};
|
||||
const pickMode = id => { pickedMode = id; try { localStorage.setItem('mode', id); } catch {} };
|
||||
// TAP ADVANCES. Each screen's tap both selects and moves on — the wizard's whole promise is one
|
||||
// TAP ADVANCES. Each screen's tap both answers and moves on — the wizard's whole promise is one
|
||||
// decision per screen, and a NEXT button under a single row of choices is a second tap for nothing.
|
||||
function buildSub() {
|
||||
modeSub.innerHTML = '';
|
||||
@@ -100,12 +105,11 @@ function buildSub() {
|
||||
document.getElementById('subTitle').textContent = t.diffs ? 'DIFFICULTY' : (t.name || '');
|
||||
if (t.diffs) for (const d of DIFFS) {
|
||||
const b = document.createElement('button');
|
||||
b.className = 'chip' + (d.id === pickedDiff ? ' on' : '');
|
||||
b.textContent = d.name;
|
||||
b.className = 'chip';
|
||||
b.innerHTML = d.name + (d.id === pickedDiff ? '<u class="last">LAST</u>' : '');
|
||||
b.addEventListener('click', () => {
|
||||
pickedDiff = d.id;
|
||||
try { localStorage.setItem('diff', d.id); } catch {}
|
||||
for (const el of modeSub.children) el.classList.toggle('on', el === b);
|
||||
summary();
|
||||
go('setup');
|
||||
});
|
||||
@@ -115,25 +119,28 @@ function buildSub() {
|
||||
const b = banner(modeOf(id), 'mode', id === pickedMode);
|
||||
b.addEventListener('click', () => {
|
||||
pickMode(id);
|
||||
for (const el of modeSub.children) el.classList.toggle('on', el === b);
|
||||
summary();
|
||||
go('setup');
|
||||
});
|
||||
modeSub.appendChild(b);
|
||||
}
|
||||
}
|
||||
for (const t of MENU) {
|
||||
const b = banner(t.mode ? modeOf(t.mode) : t, 'mode', topOf(pickedMode) === t);
|
||||
b.addEventListener('click', () => {
|
||||
// a group remembers which child you were on; entering it fresh lands on its first card
|
||||
pickMode(t.mode || (t.kids.includes(pickedMode) ? pickedMode : t.kids[0]));
|
||||
for (const el of modeList.children) el.classList.toggle('on', el === b);
|
||||
buildSub();
|
||||
summary();
|
||||
go(t.diffs || t.kids ? 'modesub' : 'setup');
|
||||
});
|
||||
modeList.appendChild(b);
|
||||
// the top posters are rebuilt on every visit so the LAST tag follows what was actually played
|
||||
function buildTops() {
|
||||
modeList.innerHTML = '';
|
||||
for (const t of MENU) {
|
||||
const b = banner(t.mode ? modeOf(t.mode) : t, 'mode', topOf(pickedMode) === t);
|
||||
b.addEventListener('click', () => {
|
||||
// a group remembers which child you were on; entering it fresh lands on its first card
|
||||
pickMode(t.mode || (t.kids.includes(pickedMode) ? pickedMode : t.kids[0]));
|
||||
buildSub();
|
||||
summary();
|
||||
go(t.diffs || t.kids ? 'modesub' : 'setup');
|
||||
});
|
||||
modeList.appendChild(b);
|
||||
}
|
||||
}
|
||||
buildTops();
|
||||
buildSub();
|
||||
|
||||
// --- tour tiles, built from the table. Same "tap selects, SERVE UP starts" rule as the court rows —
|
||||
@@ -238,7 +245,9 @@ const openStats = () => {
|
||||
for (const el of document.getElementById('stModes').children) el.classList.toggle('on', el.dataset.m === stMode);
|
||||
stats.drawStats(stPeriod, SPRV, stMode); go('stats');
|
||||
};
|
||||
document.getElementById('mStart').addEventListener('click', () => go('mode'));
|
||||
// entering a question screen rebuilds it, so the LAST tag always names the run actually played
|
||||
const goMode = () => { buildTops(); go('mode'); };
|
||||
document.getElementById('mStart').addEventListener('click', goMode);
|
||||
document.getElementById('mStats').addEventListener('click', openStats);
|
||||
document.getElementById('mRanks').addEventListener('click', () => { go('ranks'); stats.drawRanks(); });
|
||||
|
||||
@@ -291,10 +300,11 @@ for (const id of ['mBack', 'stBack', 'rkBack', 'scBack'])
|
||||
document.getElementById(id).addEventListener('click', () => go('splash'));
|
||||
// the wizard walks back the way it came: who-and-where returns to the mode's own second step when
|
||||
// it has one, the second step returns to the posters
|
||||
document.getElementById('subBack').addEventListener('click', () => go('mode'));
|
||||
document.getElementById('subBack').addEventListener('click', goMode);
|
||||
document.getElementById('sBack').addEventListener('click', () => {
|
||||
const t = topOf(pickedMode);
|
||||
go(t.diffs || t.kids ? 'modesub' : 'mode');
|
||||
if (t.diffs || t.kids) { buildSub(); go('modesub'); }
|
||||
else goMode();
|
||||
});
|
||||
document.getElementById('bsBack').addEventListener('click', () => go('secret'));
|
||||
|
||||
@@ -423,7 +433,7 @@ addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape') return go('splash');
|
||||
if (stage === 'splash') {
|
||||
if (!loaded()) return; // the boot bar hides the buttons; a key must not walk past it
|
||||
if (e.key === 'Enter' || e.key === ' ') go('mode');
|
||||
if (e.key === 'Enter' || e.key === ' ') goMode();
|
||||
else if (e.key === 's' || e.key === 'S') openStats();
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user