diff --git a/public/assets/img/hero_benedict.png b/public/assets/img/hero_benedict.png new file mode 100644 index 0000000..ff66637 Binary files /dev/null and b/public/assets/img/hero_benedict.png differ diff --git a/public/assets/img/hero_eggs.png b/public/assets/img/hero_eggs.png new file mode 100644 index 0000000..f824a3d Binary files /dev/null and b/public/assets/img/hero_eggs.png differ diff --git a/public/assets/img/hero_grill.png b/public/assets/img/hero_grill.png new file mode 100644 index 0000000..89a391d Binary files /dev/null and b/public/assets/img/hero_grill.png differ diff --git a/public/assets/img/hero_pan.png b/public/assets/img/hero_pan.png new file mode 100644 index 0000000..eca36f1 Binary files /dev/null and b/public/assets/img/hero_pan.png differ diff --git a/public/assets/img/hero_poach.png b/public/assets/img/hero_poach.png new file mode 100644 index 0000000..29f01f7 Binary files /dev/null and b/public/assets/img/hero_poach.png differ diff --git a/public/assets/img/hero_steak.png b/public/assets/img/hero_steak.png new file mode 100644 index 0000000..7da1314 Binary files /dev/null and b/public/assets/img/hero_steak.png differ diff --git a/public/assets/img/jar_bubbling.png b/public/assets/img/jar_bubbling.png new file mode 100644 index 0000000..f039fc3 Binary files /dev/null and b/public/assets/img/jar_bubbling.png differ diff --git a/public/assets/img/jar_dead.png b/public/assets/img/jar_dead.png new file mode 100644 index 0000000..59aa39e Binary files /dev/null and b/public/assets/img/jar_dead.png differ diff --git a/public/assets/img/jar_sulking.png b/public/assets/img/jar_sulking.png new file mode 100644 index 0000000..a698a0a Binary files /dev/null and b/public/assets/img/jar_sulking.png differ diff --git a/public/assets/img/who_a_child.png b/public/assets/img/who_a_child.png new file mode 100644 index 0000000..a6c3001 Binary files /dev/null and b/public/assets/img/who_a_child.png differ diff --git a/public/assets/img/who_a_man_in_a_hurry.png b/public/assets/img/who_a_man_in_a_hurry.png new file mode 100644 index 0000000..69261ed Binary files /dev/null and b/public/assets/img/who_a_man_in_a_hurry.png differ diff --git a/public/assets/img/who_a_regular.png b/public/assets/img/who_a_regular.png new file mode 100644 index 0000000..920a49f Binary files /dev/null and b/public/assets/img/who_a_regular.png differ diff --git a/public/assets/img/who_deidre.png b/public/assets/img/who_deidre.png new file mode 100644 index 0000000..5aee724 Binary files /dev/null and b/public/assets/img/who_deidre.png differ diff --git a/public/assets/img/who_ray.png b/public/assets/img/who_ray.png new file mode 100644 index 0000000..628a7cd Binary files /dev/null and b/public/assets/img/who_ray.png differ diff --git a/public/assets/img/who_someone_new.png b/public/assets/img/who_someone_new.png new file mode 100644 index 0000000..f5b9ff1 Binary files /dev/null and b/public/assets/img/who_someone_new.png differ diff --git a/public/assets/img/who_the_benedict_table.png b/public/assets/img/who_the_benedict_table.png new file mode 100644 index 0000000..1ebe91b Binary files /dev/null and b/public/assets/img/who_the_benedict_table.png differ diff --git a/public/assets/img/who_the_bloke_from_two_doors_down.png b/public/assets/img/who_the_bloke_from_two_doors_down.png new file mode 100644 index 0000000..2645bc4 Binary files /dev/null and b/public/assets/img/who_the_bloke_from_two_doors_down.png differ diff --git a/public/assets/img/who_the_chef_on_her_day_off.png b/public/assets/img/who_the_chef_on_her_day_off.png new file mode 100644 index 0000000..474f62c Binary files /dev/null and b/public/assets/img/who_the_chef_on_her_day_off.png differ diff --git a/public/assets/img/who_the_delivery_at_6am.png b/public/assets/img/who_the_delivery_at_6am.png new file mode 100644 index 0000000..dfc2eda Binary files /dev/null and b/public/assets/img/who_the_delivery_at_6am.png differ diff --git a/public/assets/img/who_the_lunch_rush.png b/public/assets/img/who_the_lunch_rush.png new file mode 100644 index 0000000..71df78c Binary files /dev/null and b/public/assets/img/who_the_lunch_rush.png differ diff --git a/public/assets/img/who_the_man_at_table_nine.png b/public/assets/img/who_the_man_at_table_nine.png new file mode 100644 index 0000000..8ba6679 Binary files /dev/null and b/public/assets/img/who_the_man_at_table_nine.png differ diff --git a/public/assets/img/who_the_pavlova_order.png b/public/assets/img/who_the_pavlova_order.png new file mode 100644 index 0000000..d4b0190 Binary files /dev/null and b/public/assets/img/who_the_pavlova_order.png differ diff --git a/scripts/gen-art-batch.sh b/scripts/gen-art-batch.sh new file mode 100755 index 0000000..dccbbe2 --- /dev/null +++ b/scripts/gen-art-batch.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# gen-art-batch.sh — the full 2D art pass on the Cloudflare FLUX lane. +# Regulars' ticket portraits, THE MOTHER's jar states, dish heroes. +# Idempotent: skips anything already in public/assets/img. +set -uo pipefail +cd "$(dirname "$0")/.." +S="${SCRATCH:-/tmp/toastsim-art}"; mkdir -p "$S" +STYLE="claymation stop-motion figurine, plain light grey background, soft studio lighting, chunky stylized game asset, waist-up portrait" +JSTYLE="claymation stop-motion style, plain light grey background, soft studio lighting, chunky stylized game asset" + +gen() { # gen + local name="$1" seed="$2" prompt="$3" + [ -f "public/assets/img/$name.png" ] && { echo "= $name exists"; return; } + ./scripts/cf-flux.py "$S/$name.raw" "$prompt" 8 "$seed" >/dev/null \ + && sips -s format png -Z 768 "$S/$name.raw" --out "public/assets/img/$name.png" >/dev/null 2>&1 \ + && echo "+ $name" || echo "! $name FAILED" +} + +# The regulars — one fixed seed each so reruns keep the same person. +gen who_deidre 101 "$STYLE, elderly woman with lavender-grey perm, floral cardigan, large round glasses on a chain, clutching a small handbag, expectant pursed smile" +gen who_ray 102 "$STYLE, middle-aged working man in a flat cap and worn brown work jacket, stubble, mug of tea in hand, easy-going squint" +gen who_a_child 103 "$STYLE, small child on tiptoes at a counter, oversized striped jumper, gap-toothed grin, holding out a fistful of coins" +gen who_a_man_in_a_hurry 104 "$STYLE, businessman with loosened tie and rumpled suit, briefcase under one arm, checking his wristwatch, harried expression" +gen who_a_regular 105 "$STYLE, comfortable older man in a knitted vest with a folded newspaper under his arm, content half-smile, coffee mug" +gen who_someone_new 106 "$STYLE, young woman with a short bob and denim jacket, looking around uncertainly, holding a menu, hopeful nervous smile" +gen who_the_bloke_from_two_doors_down 107 "$STYLE, unshaven man in a tartan dressing gown over day clothes, slippers, holding his own mug from home, sheepish grin" +gen who_the_chef_on_her_day_off 108 "$STYLE, woman with hair in a tight bun, chef whites jacket open over a band t-shirt, arms crossed, appraising professional stare" +gen who_the_delivery_at_6am 109 "$STYLE, courier in a hi-vis vest and beanie mid-yawn, cardboard box under one arm, heavy eyelids" +gen who_the_lunch_rush 110 "$STYLE, three impatient office workers crowded shoulder to shoulder, all checking phones and watches, tight group" +gen who_the_man_at_table_nine 111 "$STYLE, mysterious lone man in a long grey overcoat, fedora resting on the table edge, steepled fingers, unreadable face" +gen who_the_pavlova_order 112 "$STYLE, grand formidable elderly woman in pearls and a silk scarf, chin high, gloved hands folded, expecting perfection" +gen who_the_benedict_table 113 "$STYLE, refined woman in a cream blazer with gold earrings, holding a menu closed, patient knowing smile" + +# THE MOTHER — three states of one jar. +gen jar_bubbling 201 "$JSTYLE, single glass mason jar of sourdough starter, cloth cap tied with string, contents risen and bubbly, lively froth over the rim, warm and healthy" +gen jar_sulking 202 "$JSTYLE, single glass mason jar of sourdough starter, cloth cap tied with string, contents flat and sunken with a thin grey liquid layer on top, gloomy" +gen jar_dead 203 "$JSTYLE, single glass mason jar of long-dead sourdough starter, cloth cap askew, contents collapsed dark and crusted, a small wilted flag stuck in it, funereal" + +# Dish heroes — the plate on the scorecard's order line. +gen hero_grill 301 "$JSTYLE, plate of chargrilled mushroom skewers with proper dark grill marks, single plate centered" +gen hero_pan 302 "$JSTYLE, cast iron pan with golden butter-basted mushrooms glistening, single pan centered" +gen hero_steak 303 "$JSTYLE, sliced rib eye steak on a wooden board, rosy pink centre, resting juices, single board centered" +gen hero_eggs 304 "$JSTYLE, glass bowl of whipped glossy egg whites beside three brown eggshells, single bowl centered" +gen hero_poach 305 "$JSTYLE, single perfect poached egg on a slotted spoon, white silky and intact, droplet falling" +gen hero_benedict 306 "$JSTYLE, eggs benedict on a toasted muffin, hollandaise dripping down, single plate centered" + +echo done diff --git a/src/game/game.ts b/src/game/game.ts index 40d2183..aaeb198 100644 --- a/src/game/game.ts +++ b/src/game/game.ts @@ -31,6 +31,7 @@ import { DAYS, proceduralOrder, nameBrowning, prepAsk, type Order, type PrepStep import { el, Panel } from '../ui/hud'; import { Title } from '../ui/title'; import { eye } from '../ui/eye'; +import { assetUrl } from '../scenes/assets'; const SAVE_KEY = 'toastsim.save'; @@ -1088,6 +1089,12 @@ export class Game { this.ticketEl.innerHTML = ''; el('div', 'ticket-day', this.ticketEl, `DAY ${o.day}`); el('div', 'ticket-who', this.ticketEl, o.who); + // The regular's face, clipped to the ticket. The inspector uses his own. + const faceImg = el('img', 'ticket-face', this.ticketEl) as HTMLImageElement; + const slug = o.who.replace(', again', '').toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, ''); + faceImg.alt = ''; + faceImg.src = assetUrl(o.who.includes('inspector') ? '/assets/img/judge_neutral.png' : `/assets/img/who_${slug}.png`); + faceImg.onerror = () => faceImg.remove(); // a face we never made — no frame, no fuss if (o.day % 10 === 0 && !this.dailyDate) el('div', 'ticket-critic', this.ticketEl, 'THE CRITIC is in today. The flaw will be the headline.'); if (this.rushDay(o.day) && !this.dailyDate) el('div', 'ticket-critic', this.ticketEl, this.rushFirst !== null ? 'TICKET TWO — the first is dying on the pass.' : 'RUSH HOUR — two tickets on the rail. The first will wait for the second.'); const demos = (window as unknown as { __t?: { demos?: Record } }).__t?.demos; diff --git a/src/scenes/judge.ts b/src/scenes/judge.ts index a6398ae..001bdba 100644 --- a/src/scenes/judge.ts +++ b/src/scenes/judge.ts @@ -194,6 +194,15 @@ export class JudgeView implements View { // Grill and bruschetta days don't have a browning/spread to name — show who // asked and what for, not the unread toast defaults. + // The dish itself, in miniature, beside the ask. + const heroKey = order.benedict ? 'benedict' : order.poach ? 'poach' : order.eggs ? 'eggs' : order.steak ? 'steak' : order.grill ? 'grill' : order.pan ? 'pan' : null; + this.orderEl.parentElement?.querySelector('.judge-hero')?.remove(); + if (heroKey) { + const hero = el('img', 'judge-hero', this.orderEl.parentElement ?? this.orderEl) as HTMLImageElement; + hero.alt = ''; + hero.src = assetUrl(`/assets/img/hero_${heroKey}.png`); + this.orderEl.parentElement?.insertBefore(hero, this.orderEl); + } this.orderEl.textContent = order.grill ? `Day ${order.day} · ${order.who} asked for the barbie` : order.benedict diff --git a/src/style.css b/src/style.css index a9d12e3..8a2d36a 100644 --- a/src/style.css +++ b/src/style.css @@ -806,3 +806,33 @@ body:has(#ui > .title) #touchpad { display: none; } color: #b8d89a; transition: all 0.2s; } + +/* The regular's face on the ticket. */ +.ticket-face { + position: absolute; + top: 10px; + right: 12px; + width: 54px; + height: 54px; + object-fit: cover; + border-radius: 50%; + border: 2px solid #cbb89a; + background: #e8dcc4; +} +.jar-img { + width: 26px; + height: 26px; + object-fit: cover; + border-radius: 6px; + vertical-align: middle; + margin-right: 8px; +} +.judge-hero { + width: 52px; + height: 52px; + object-fit: cover; + border-radius: 10px; + border: 2px solid #3a2c20; + float: right; + margin: 0 0 6px 10px; +} diff --git a/src/ui/title.ts b/src/ui/title.ts index bc49c54..e0712f9 100644 --- a/src/ui/title.ts +++ b/src/ui/title.ts @@ -52,8 +52,13 @@ export class Title { if (save.starter) { const st = save.starter; const jar = el('button', 'btn ghost jar-btn', txt, ''); + const jarImg = el('img', 'jar-img', jar) as HTMLImageElement; + jarImg.alt = ''; + const jarTxt = el('span', undefined, jar, ''); const label = () => { - const gap = save.maxDay - st.fed; + const gap0 = save.maxDay - st.fed; + jarImg.src = assetUrl(`/assets/img/jar_${gap0 >= 5 ? 'dead' : gap0 >= 3 ? 'sulking' : 'bubbling'}.png`); + const gap = gap0; const name = st.deaths > 0 ? `THE MOTHER (${['second', 'third', 'fourth', 'umpteenth'][Math.min(st.deaths - 1, 3)]} of her name)` : 'THE MOTHER'; if (gap >= 5) return `\u{1FAD9} ${name} has gone still. begin again \u2014 she forgives, mostly`; if (gap === 0) return `\u{1FAD9} ${name} \u2014 fed. bubbling like gossip`; @@ -61,13 +66,13 @@ export class Title { if (gap === 4) return `\u{1FAD9} ${name} \u2014 one day from the end. FEED HER NOW`; return `\u{1FAD9} ${name} \u2014 hungry (${gap} days). the crumb suffers`; }; - jar.textContent = label(); + jarTxt.textContent = label(); jar.addEventListener('click', () => { const gap = save.maxDay - st.fed; if (gap >= 5) { st.deaths++; st.born = save.maxDay; } st.fed = save.maxDay; onFeed?.(); - jar.textContent = label(); + jarTxt.textContent = label(); jar.classList.add('fed'); window.setTimeout(() => jar.classList.remove('fed'), 700); });