The setup became a wizard: one decision per screen, tap advances
"the mode setup is really overwhelming now" — three posters plus a sub-row plus player tiles plus court meters was a wall. Discussed, Oliver picked the wizard with tap-advance over next-buttons and the accordion: PICK A GAME (three posters, now 148px tall — alone on a screen they get to be posters), then the mode's own second step only when it has one (classic: DIFFICULTY chips stacked tall; zombie tennis: ARCADE and THE TOURNAMENT as full posters; gore skips straight through), then MATCH SETUP holding player + court + SERVE UP with the mode riding the summary footer. Tapping selects AND advances — one decision per screen is the wizard's whole promise, and a NEXT under a single row of choices is a second tap for nothing. Back walks the chain the way it came (who-and-where returns to the second step only when the mode has one). Every step comes up pre-selected from last time, so a returning player is three taps from serving. Three new stages on the intro's existing data-stage switcher; splash Enter goes to 'mode' now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e4c917e23d
commit
65be60b139
2 files changed
+45
-21
No files matched your search
+28
-17
@@ -140,11 +140,13 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') {
|
|||||||
/* ONE overlay, four panels, swapped by #intro[data-stage]. Keeping the backdrop and the card frame
|
/* ONE overlay, four panels, swapped by #intro[data-stage]. Keeping the backdrop and the card frame
|
||||||
alive across every screen is the point — a screen that unmounts the whole overlay flashes the
|
alive across every screen is the point — a screen that unmounts the whole overlay flashes the
|
||||||
court through between menus. */
|
court through between menus. */
|
||||||
#splash, #setup, #stats, #ranks, #newbest, #secret, #bosses { display: none; }
|
#splash, #setup, #stats, #ranks, #newbest, #secret, #bosses, #stmode, #stsub { display: none; }
|
||||||
#intro[data-stage="splash"] #splash { display: flex; flex-direction: column; flex: 1; }
|
#intro[data-stage="splash"] #splash { display: flex; flex-direction: column; flex: 1; }
|
||||||
/* flex COLUMN, not block: it is what lets the footer's margin-top:auto reach the bottom of a
|
/* flex COLUMN, not block: it is what lets the footer's margin-top:auto reach the bottom of a
|
||||||
screen whose content does not fill it */
|
screen whose content does not fill it */
|
||||||
#intro[data-stage="setup"] #setup,
|
#intro[data-stage="setup"] #setup,
|
||||||
|
#intro[data-stage="mode"] #stmode,
|
||||||
|
#intro[data-stage="modesub"] #stsub,
|
||||||
#intro[data-stage="stats"] #stats,
|
#intro[data-stage="stats"] #stats,
|
||||||
#intro[data-stage="ranks"] #ranks,
|
#intro[data-stage="ranks"] #ranks,
|
||||||
#intro[data-stage="secret"] #secret,
|
#intro[data-stage="secret"] #secret,
|
||||||
@@ -235,16 +237,15 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') {
|
|||||||
at cream-on-crowd was the report from the first live look */
|
at cream-on-crowd was the report from the first live look */
|
||||||
#modeList i, #modeSub i { color: #fff; font-size: 10px; margin-bottom: 0;
|
#modeList i, #modeSub i { color: #fff; font-size: 10px; margin-bottom: 0;
|
||||||
text-shadow: 0 1px 0 #000, 1px 0 0 #000, -1px 0 0 #000, 0 -1px 0 #000, 0 0 7px #000; }
|
text-shadow: 0 1px 0 #000, 1px 0 0 #000, -1px 0 0 #000, 0 -1px 0 #000, 0 0 7px #000; }
|
||||||
/* THE SECOND STEP (config.MENU): the picked poster's own choices, inset so they read as its
|
/* THE SECOND STEP (config.MENU) has its own SCREEN in the wizard: sub-mode cards are
|
||||||
children. Sub-mode cards are the same poster at ~2/3 height, centred on their scene rather
|
full-height posters centred on their scene, and the difficulty chips are plain acid-outline
|
||||||
than bottom-anchored (a 64px window off the bottom of these scenes is all court and shins);
|
buttons stacked tall — art for EASY/MEDIUM/HARD would be forced, and the chip reads faster. */
|
||||||
the difficulty chips are plain acid-outline buttons — art for EASY/MEDIUM/HARD would be
|
/* alone on their own screens, the posters get room to be posters */
|
||||||
forced, and the chip row reads faster anyway. */
|
#stmode button.mode, #modeSub button.mode { min-height: 148px; }
|
||||||
#modeSub { display: flex; flex-wrap: wrap; gap: 8px; padding-left: 14px; margin: 0 0 8px; }
|
#modeSub { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
#modeSub:empty { display: none; }
|
#modeSub button.mode { background-position: center; }
|
||||||
#modeSub button.mode.sm { flex: 1 1 100%; min-height: 64px; margin: 0; background-position: center; }
|
#modeSub button.chip { flex: 1 1 100%; padding: 22px 0; background: var(--panel); border: 2px solid var(--line);
|
||||||
#modeSub button.chip { flex: 1; padding: 12px 0; background: var(--panel); border: 2px solid var(--line);
|
color: var(--cream); font: 400 13px Silkscreen, monospace; letter-spacing: 2px;
|
||||||
color: var(--cream); font: 400 11px Silkscreen, monospace; letter-spacing: 1px;
|
|
||||||
cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
||||||
#modeSub button.chip.on { border-color: var(--acid); color: var(--acid); background: rgba(188,212,85,0.09); }
|
#modeSub button.chip.on { border-color: var(--acid); color: var(--acid); background: rgba(188,212,85,0.09); }
|
||||||
#courtList button.court { display: flex; align-items: center; gap: 11px; width: 100%;
|
#courtList button.court { display: flex; align-items: center; gap: 11px; width: 100%;
|
||||||
@@ -378,14 +379,24 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') {
|
|||||||
</div>
|
</div>
|
||||||
<!-- tour tiles and court rows are both built from tables in main.js (config.TOURS / config.COURTS):
|
<!-- tour tiles and court rows are both built from tables in main.js (config.TOURS / config.COURTS):
|
||||||
adding either is a table row, never markup -->
|
adding either is a table row, never markup -->
|
||||||
|
<!-- THE SETUP IS A WIZARD now (Oliver: one screen of posters + chips + tiles + meters was
|
||||||
|
"really overwhelming"). Three stages, tap advances: the mode posters, then the mode's own
|
||||||
|
second step when it has one (classic: difficulty, zombie tennis: arcade or the cup), then
|
||||||
|
who-and-where with SERVE UP. Every step comes up pre-selected from last time, so a
|
||||||
|
returning player is three taps from serving. -->
|
||||||
|
<div id="stmode">
|
||||||
|
<div class="hdr"><button class="chev" id="mBack">‹</button><h2>PICK A GAME</h2></div>
|
||||||
|
<div id="modeList"></div>
|
||||||
|
</div>
|
||||||
|
<div id="stsub">
|
||||||
|
<div class="hdr"><button class="chev" id="subBack">‹</button><h2 id="subTitle">DIFFICULTY</h2></div>
|
||||||
|
<div id="modeSub"></div>
|
||||||
|
</div>
|
||||||
<div id="setup">
|
<div id="setup">
|
||||||
<div class="hdr"><button class="chev" id="sBack">‹</button><h2>MATCH SETUP</h2></div>
|
<div class="hdr"><button class="chev" id="sBack">‹</button><h2>MATCH SETUP</h2></div>
|
||||||
<h3>01 · MODE</h3>
|
<h3>01 · PLAYER</h3>
|
||||||
<div id="modeList"></div>
|
|
||||||
<div id="modeSub"></div>
|
|
||||||
<h3>02 · PLAYER</h3>
|
|
||||||
<div id="tours"></div>
|
<div id="tours"></div>
|
||||||
<h3>03 · COURT</h3>
|
<h3>02 · COURT</h3>
|
||||||
<div id="courtList"></div>
|
<div id="courtList"></div>
|
||||||
<div class="foot">
|
<div class="foot">
|
||||||
<p id="sSum"></p>
|
<p id="sSum"></p>
|
||||||
@@ -456,6 +467,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="src/main.js?v=233"></script>
|
<script type="module" src="src/main.js?v=234"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+17
-4
@@ -92,9 +92,12 @@ const banner = (card, cls, on) => {
|
|||||||
return b;
|
return b;
|
||||||
};
|
};
|
||||||
const pickMode = id => { pickedMode = id; try { localStorage.setItem('mode', id); } catch {} };
|
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
|
||||||
|
// decision per screen, and a NEXT button under a single row of choices is a second tap for nothing.
|
||||||
function buildSub() {
|
function buildSub() {
|
||||||
modeSub.innerHTML = '';
|
modeSub.innerHTML = '';
|
||||||
const t = topOf(pickedMode);
|
const t = topOf(pickedMode);
|
||||||
|
document.getElementById('subTitle').textContent = t.diffs ? 'DIFFICULTY' : (t.name || '');
|
||||||
if (t.diffs) for (const d of DIFFS) {
|
if (t.diffs) for (const d of DIFFS) {
|
||||||
const b = document.createElement('button');
|
const b = document.createElement('button');
|
||||||
b.className = 'chip' + (d.id === pickedDiff ? ' on' : '');
|
b.className = 'chip' + (d.id === pickedDiff ? ' on' : '');
|
||||||
@@ -104,15 +107,17 @@ function buildSub() {
|
|||||||
try { localStorage.setItem('diff', d.id); } catch {}
|
try { localStorage.setItem('diff', d.id); } catch {}
|
||||||
for (const el of modeSub.children) el.classList.toggle('on', el === b);
|
for (const el of modeSub.children) el.classList.toggle('on', el === b);
|
||||||
summary();
|
summary();
|
||||||
|
go('setup');
|
||||||
});
|
});
|
||||||
modeSub.appendChild(b);
|
modeSub.appendChild(b);
|
||||||
}
|
}
|
||||||
else if (t.kids) for (const id of t.kids) {
|
else if (t.kids) for (const id of t.kids) {
|
||||||
const b = banner(modeOf(id), 'mode sm', id === pickedMode);
|
const b = banner(modeOf(id), 'mode', id === pickedMode);
|
||||||
b.addEventListener('click', () => {
|
b.addEventListener('click', () => {
|
||||||
pickMode(id);
|
pickMode(id);
|
||||||
for (const el of modeSub.children) el.classList.toggle('on', el === b);
|
for (const el of modeSub.children) el.classList.toggle('on', el === b);
|
||||||
summary();
|
summary();
|
||||||
|
go('setup');
|
||||||
});
|
});
|
||||||
modeSub.appendChild(b);
|
modeSub.appendChild(b);
|
||||||
}
|
}
|
||||||
@@ -125,6 +130,7 @@ for (const t of MENU) {
|
|||||||
for (const el of modeList.children) el.classList.toggle('on', el === b);
|
for (const el of modeList.children) el.classList.toggle('on', el === b);
|
||||||
buildSub();
|
buildSub();
|
||||||
summary();
|
summary();
|
||||||
|
go(t.diffs || t.kids ? 'modesub' : 'setup');
|
||||||
});
|
});
|
||||||
modeList.appendChild(b);
|
modeList.appendChild(b);
|
||||||
}
|
}
|
||||||
@@ -232,7 +238,7 @@ const openStats = () => {
|
|||||||
for (const el of document.getElementById('stModes').children) el.classList.toggle('on', el.dataset.m === stMode);
|
for (const el of document.getElementById('stModes').children) el.classList.toggle('on', el.dataset.m === stMode);
|
||||||
stats.drawStats(stPeriod, SPRV, stMode); go('stats');
|
stats.drawStats(stPeriod, SPRV, stMode); go('stats');
|
||||||
};
|
};
|
||||||
document.getElementById('mStart').addEventListener('click', () => go('setup'));
|
document.getElementById('mStart').addEventListener('click', () => go('mode'));
|
||||||
document.getElementById('mStats').addEventListener('click', openStats);
|
document.getElementById('mStats').addEventListener('click', openStats);
|
||||||
document.getElementById('mRanks').addEventListener('click', () => { go('ranks'); stats.drawRanks(); });
|
document.getElementById('mRanks').addEventListener('click', () => { go('ranks'); stats.drawRanks(); });
|
||||||
|
|
||||||
@@ -281,8 +287,15 @@ nbName.addEventListener('keydown', e => { if (e.key === 'Enter') document.getEle
|
|||||||
// ZOMBIE TENNIS did nothing at all. A default parameter only applies to `undefined`, and a click
|
// ZOMBIE TENNIS did nothing at all. A default parameter only applies to `undefined`, and a click
|
||||||
// handler always passes something.
|
// handler always passes something.
|
||||||
document.getElementById('cStart').addEventListener('click', () => start());
|
document.getElementById('cStart').addEventListener('click', () => start());
|
||||||
for (const id of ['sBack', 'stBack', 'rkBack', 'scBack'])
|
for (const id of ['mBack', 'stBack', 'rkBack', 'scBack'])
|
||||||
document.getElementById(id).addEventListener('click', () => go('splash'));
|
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('sBack').addEventListener('click', () => {
|
||||||
|
const t = topOf(pickedMode);
|
||||||
|
go(t.diffs || t.kids ? 'modesub' : 'mode');
|
||||||
|
});
|
||||||
document.getElementById('bsBack').addEventListener('click', () => go('secret'));
|
document.getElementById('bsBack').addEventListener('click', () => go('secret'));
|
||||||
|
|
||||||
// ---- THE BACK ROOM: five taps in the TOP-LEFT CORNER of the splash (#hot, an invisible box in
|
// ---- THE BACK ROOM: five taps in the TOP-LEFT CORNER of the splash (#hot, an invisible box in
|
||||||
@@ -410,7 +423,7 @@ addEventListener('keydown', e => {
|
|||||||
if (e.key === 'Escape') return go('splash');
|
if (e.key === 'Escape') return go('splash');
|
||||||
if (stage === 'splash') {
|
if (stage === 'splash') {
|
||||||
if (!loaded()) return; // the boot bar hides the buttons; a key must not walk past it
|
if (!loaded()) return; // the boot bar hides the buttons; a key must not walk past it
|
||||||
if (e.key === 'Enter' || e.key === ' ') go('setup');
|
if (e.key === 'Enter' || e.key === ' ') go('mode');
|
||||||
else if (e.key === 's' || e.key === 'S') openStats();
|
else if (e.key === 's' || e.key === 'S') openStats();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user