From 317c78be71d74d155b7a60bf10ff2dfe175548f9 Mon Sep 17 00:00:00 2001 From: type-two Date: Tue, 1 Sep 2026 10:43:49 +1000 Subject: [PATCH] First-timer coaching, an escape from the judge, and a cleaner polaroid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - judge screen: the foot row now wraps (NEXT ORDER was pushed off-viewport on narrower windows — the game looked unfinishable), ENTER advances after a 0.6s guard, and the button says so - polaroid: dropped the partly.party URL stamp - kitchen: first session gets the phase hints at coaching volume (gold, pulsing) until the first slice is served Co-Authored-By: Claude Fable 5 --- src/scenes/judge.ts | 11 +++++++---- src/scenes/kitchen.ts | 8 ++++++++ src/style.css | 21 +++++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/scenes/judge.ts b/src/scenes/judge.ts index 539f71b..bdef648 100644 --- a/src/scenes/judge.ts +++ b/src/scenes/judge.ts @@ -20,6 +20,9 @@ export class JudgeView implements View { private slice: Slice | null = null; private spin = 0; private heatmap: 0 | 1 | 2 = 0; + /** Seconds since the scorecard appeared — the leftover ENTER that served the + * toast must not also dismiss the verdict before anyone reads it. */ + private age = 0; private panel: Panel; private cardEl!: HTMLElement; @@ -98,7 +101,7 @@ export class JudgeView implements View { () => undefined, ); }); - this.nextBtn = el('button', 'btn', btns, 'NEXT ORDER'); + this.nextBtn = el('button', 'btn', btns, 'NEXT ORDER ⏎'); this.nextBtn.addEventListener('click', () => this.onNext?.()); } @@ -161,9 +164,6 @@ export class JudgeView implements View { row += w + ' '; } if (row.trim() && y <= H - 40) g.fillText((y === 762 ? '“' : '') + row.trim() + '”', 34, y); - g.font = '16px Georgia, serif'; - g.fillStyle = '#8a7f72'; - g.fillText('partly.party/toastsim', W - 210, H - 22); // Hand it over. const a = document.createElement('a'); a.href = out.toDataURL('image/png'); @@ -181,6 +181,7 @@ export class JudgeView implements View { eye.hide(); // the full portrait takes over from the corner glance this.slice = slice; this.spin = 0; + this.age = 0; this.heatmap = 0; slice.setHeatmap(0); slice.setPresentation(true); @@ -334,6 +335,8 @@ export class JudgeView implements View { } update(dt: number): void { + this.age += dt; + if (this.age > 0.6 && this.app.input.justPressed('Enter')) this.onNext?.(); this.spin += dt * 0.42; if (this.slice) this.slice.mesh.rotation.y = this.spin; this.app.camera.position.set(0.15, 2.35, 3.5); diff --git a/src/scenes/kitchen.ts b/src/scenes/kitchen.ts index c34b834..f2e6b88 100644 --- a/src/scenes/kitchen.ts +++ b/src/scenes/kitchen.ts @@ -229,6 +229,10 @@ export class KitchenView implements View { this.cueEl = el('div', 'cue', p, 'smells like bread'); this.hintEl = el('div', 'hint', p, 'SPACE — push the lever down'); + // Never-served-a-slice players get the hints at coaching volume. + try { + if (!localStorage.getItem('toastsim-coached')) this.hintEl.classList.add('coach'); + } catch { /* storage blocked — quiet hints are fine */ } this.setSpread(this.spreadId); } @@ -306,6 +310,10 @@ export class KitchenView implements View { serve(): void { if (this.phase !== 'spreading') return; + if (this.hintEl.classList.contains('coach')) { + this.hintEl.classList.remove('coach'); + try { localStorage.setItem('toastsim-coached', '1'); } catch { /* fine */ } + } this.onServed?.(); } diff --git a/src/style.css b/src/style.css index 136d285..1d49593 100644 --- a/src/style.css +++ b/src/style.css @@ -117,6 +117,20 @@ canvas#c { min-height: 14px; } +/* First-ever session: same hints, but loud enough to actually teach. + The class comes off for good once the first slice has been served. */ +.toast-panel .hint.coach { + font-size: 15px; + font-weight: 700; + color: var(--gold); + animation: coach-pulse 1.6s ease-in-out infinite; +} + +@keyframes coach-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.55; } +} + /* Pressure gauge: fill = what your wrist is making, gold = what the spread needs to flow, red = where spreading becomes scraping. */ .toast-panel .gauge { @@ -379,6 +393,11 @@ canvas#c { display: flex; align-items: center; justify-content: space-between; + /* Four buttons plus the stamp outgrow the 380px column on plenty of + windows; without wrap, NEXT ORDER is shoved past the viewport edge + and the game looks unfinishable. */ + flex-wrap: wrap; + row-gap: 10px; margin-top: 16px; } @@ -416,6 +435,8 @@ canvas#c { .judge-btns { display: flex; gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; } .btn {