The mode buttons became posters: four bright banner scenes, captions on top
Oliver's design call for the setup screen: the mode buttons should POP — a bright full-bleed background symbolising each game mode, the description written over it. Four scenes rolled through the _famroll OpenRouter route, each anchored on its mode's own sprite for style, no text baked in: CLASSIC a sunny sports-poster serve under a blue summer sky ZOMBIE the turned opponent at an acid-green floodlit night net CUP the champion's silhouette looming over the gold trophy GORE the horde pouring over the net into a blood-red sunset Installed as mode_*.png at a third of the 2K roll, quantised to 128 colours (2.1 MB of menu art became 265 KB; pixel art quantises for free). The button is the banner now: art + scrim gradient as one background stack, caption bottom-left with a text shadow, and the unpicked modes sit desaturated and dimmed so the chosen one is the thing that glows — the menu's own "one bright thing means selected" rule, applied to pictures. The sprite-tile row and its `tile` field are gone from MODES; raw 2K rolls kept in _refcheck/modebtn for re-cuts. SPRV 114 -> 115, main.js v229. Verified in the browser: draw, select, summary line, no console errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d87fbbda56
commit
954f5e15bf
8 files changed
+31
-18
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
+17
-8
@@ -219,16 +219,25 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') {
|
||||
letter-spacing: 1px; }
|
||||
/* a row, not a stacked tile: three 3:2 tiles stacked fill a phone twice over, and the art still
|
||||
reads at this size. -webkit-tap-highlight-color kills iOS's blue flash over the pixel art. */
|
||||
/* MODE tiles reuse the court row wholesale — same box, same selected state. Only the picture
|
||||
differs: a court tile is a photo of a court and crops fine, a mode tile is a SPRITE and must be
|
||||
shown whole, so it gets `contain` and a dark plate to stand on. */
|
||||
#modeList img { object-fit: contain !important; background: rgba(0,0,0,0.35); }
|
||||
#courtList button.court, #modeList button.court { display: flex; align-items: center; gap: 11px; width: 100%;
|
||||
/* MODE buttons are the BANNER row: the whole button is a bright scene of its mode (main.js sets
|
||||
the art + a scrim gradient as the background stack) with the caption written over the bottom.
|
||||
Unpicked buttons sit dimmed and desaturated so the chosen mode is the one that glows — the
|
||||
same "one bright thing means selected" rule the menu palette already follows. */
|
||||
#modeList button.mode { display: flex; align-items: flex-end; width: 100%; min-height: 78px;
|
||||
margin: 0 0 8px; padding: 7px 9px; background-size: cover; background-position: center 30%;
|
||||
image-rendering: pixelated; border: 2px solid var(--line); color: inherit; font: inherit;
|
||||
text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
|
||||
filter: saturate(0.45) brightness(0.62); transition: filter 0.15s; }
|
||||
#modeList button.mode.on { border-color: var(--acid); filter: none; }
|
||||
#modeList b { text-shadow: 0 1px 0 #000, 0 0 6px rgba(0,0,0,0.9); }
|
||||
#modeList i { color: var(--cream); opacity: 0.8; margin-bottom: 0;
|
||||
text-shadow: 0 1px 0 #000, 0 0 6px rgba(0,0,0,0.9); }
|
||||
#courtList button.court { display: flex; align-items: center; gap: 11px; width: 100%;
|
||||
margin: 0 0 8px; padding: 7px; background: var(--panel);
|
||||
border: 2px solid var(--line); color: inherit; font: inherit; text-align: left;
|
||||
cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
||||
#courtList button.court.on, #modeList button.court.on { border-color: var(--acid); background: rgba(188,212,85,0.09); }
|
||||
#courtList img, #modeList img { width: 92px; height: 66px; display: block; image-rendering: pixelated; flex: none;
|
||||
#courtList button.court.on { border-color: var(--acid); background: rgba(188,212,85,0.09); }
|
||||
#courtList img { width: 92px; height: 66px; display: block; image-rendering: pixelated; flex: none;
|
||||
object-fit: cover; }
|
||||
#courtList .cb, #modeList .cb { flex: 1; min-width: 0; }
|
||||
#courtList b, #modeList b { display: block; font: 400 12px Silkscreen, monospace; color: var(--cream); letter-spacing: 0.5px; }
|
||||
@@ -431,6 +440,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="src/main.js?v=228"></script>
|
||||
<script type="module" src="src/main.js?v=229"></script>
|
||||
</body>
|
||||
</html>
|
||||
+8
-6
@@ -629,13 +629,15 @@ export const COURTS = [
|
||||
// classic — real tennis, best of TUNE.classicSets sets, nothing ever turns
|
||||
// zombie — the exhibition match (beats.startMatch): one game per opponent, blood only
|
||||
// gore — the original: the opening match TURNS and you survive what walks on
|
||||
// `tile` is an existing sprite rather than new art on purpose — each one is the thing you will be
|
||||
// looking at for the whole mode, which is a better advert than a drawn icon of it.
|
||||
// `art` is a full-bleed banner scene behind the whole button (Oliver's call over the sprite tiles,
|
||||
// 2026-08-14: "bright backgrounds symbolising the individual game mode with the descriptions
|
||||
// written on top"). Generated through the _famroll OpenRouter route, one scene per mode, no text
|
||||
// baked in — the button's own name/sub render over a scrim, so the caption stays crisp.
|
||||
export const MODES = [
|
||||
{ id: 'classic', name: 'CLASSIC TENNIS', sub: 'REAL TENNIS · BEST OF THREE SETS', tile: 'o_swing1' },
|
||||
{ id: 'zombie', name: 'ZOMBIE TENNIS', sub: 'ONE GAME PER OPPONENT · BLOOD ONLY', tile: 'z_r1' },
|
||||
{ id: 'cup', name: 'THE TOURNAMENT', sub: 'FOUR ROUNDS · THE CHAMPION WAITS', tile: 'z_champ1' },
|
||||
{ id: 'gore', name: 'SPLATTER GORE', sub: 'THE MATCH TURNS · SURVIVE IT', tile: 'z_boss1' },
|
||||
{ id: 'classic', name: 'CLASSIC TENNIS', sub: 'REAL TENNIS · BEST OF THREE SETS', art: 'mode_classic' },
|
||||
{ id: 'zombie', name: 'ZOMBIE TENNIS', sub: 'ONE GAME PER OPPONENT · BLOOD ONLY', art: 'mode_zombie' },
|
||||
{ id: 'cup', name: 'THE TOURNAMENT', sub: 'FOUR ROUNDS · THE CHAMPION WAITS', art: 'mode_cup' },
|
||||
{ id: 'gore', name: 'SPLATTER GORE', sub: 'THE MATCH TURNS · SURVIVE IT', art: 'mode_gore' },
|
||||
];
|
||||
|
||||
// THE TOURNAMENT (Oliver's pick over the endless ladder, 2026-08-13): four rounds up the club's
|
||||
|
||||
+5
-3
@@ -80,9 +80,11 @@ let pickedMode = remembered('mode') || MODES[0].id;
|
||||
const modeList = document.getElementById('modeList');
|
||||
for (const m of MODES) {
|
||||
const b = document.createElement('button');
|
||||
b.className = 'court' + (m.id === pickedMode ? ' on' : '');
|
||||
b.innerHTML = `<img src="assets/${m.tile}.png?v=${SPRV}" alt=""><span class="cb">`
|
||||
+ `<b>${m.name}</b><i>${m.sub}</i></span>`;
|
||||
b.className = 'mode' + (m.id === pickedMode ? ' on' : '');
|
||||
// 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/${m.art}.png?v=${SPRV})`;
|
||||
b.innerHTML = `<span class="cb"><b>${m.name}</b><i>${m.sub}</i></span>`;
|
||||
b.addEventListener('click', () => {
|
||||
pickedMode = m.id;
|
||||
try { localStorage.setItem('mode', m.id); } catch {} // come back to what you were playing
|
||||
|
||||
+1
-1
@@ -221,7 +221,7 @@ function farB(z) {
|
||||
// Asset cache-buster — bump with main.js?v=N (see the img.src note below). EXPORTED because the
|
||||
// menu screens hang <img> tags on the same files, and two copies of this number is one copy too
|
||||
// many: the one that does not get bumped is the one that serves a cached 404.
|
||||
export const SPRV = 114;
|
||||
export const SPRV = 115;
|
||||
// The boot bar in main.js watches this. Nothing here CHANGES when things load — every sprite was
|
||||
// already requested at import — it just makes the wait visible and holds the menu back until the
|
||||
// last sheet has landed. See the onerror note below for the other half of "sometimes missing".
|
||||
|
||||
Reference in new issue
Block a user