THE VESSELS: the benches get real pots, baskets and a slotted spoon

The MODELBEAST station lane finished — 14 hero props, all local, all
free — and the single-instance vessels are now wired into the benches
that were still cooking in grey primitives:

- the deep fryer: a steel pot with handles, a wire basket that rides the
  lift animation, and a proper drain rack stage left
- the poach: a perforated slotted spoon the egg actually sits IN
- pasta night: a real stockpot, with the salt visibly clouding the water

Per-piece food (wings, chips) stays procedural on purpose: their colour
IS the gameplay read — raw to golden to regret — and a textured mesh
tinted per-frame reads as mud. Fidelity loses to legibility.

Three fixes the screenshots forced, none of which guessing would have
found:
- The oil disc was sized to the understudy cylinder, so it poked out
  past the real pot's narrower rim like a yellow skirt. Refitted.
- The basket sat below the oil line — invisible, and a basket you
  cannot see is a basket you cannot judge. Raised proud, handle kept.
- The spoon is 1.9 long in Z with its bowl at one END, so centring the
  model parked the bowl a full half-length away from the egg. Measured
  the bounding box in-browser and shifted by half the length; the egg
  rides the bowl now.

And one prop rejected: the generated egg carton ships with six eggs
baked into the mesh, and that bench is entirely about which eggs are
YOURS — draggable, float-tested, state-coloured. A carton that already
has eggs in it is a lie the player has to see past. An empty tray is
queued instead.

Verified after: chips 10.0, poach 9.9, pasta 10.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-26 18:41:59 +10:00
parent cff6a50f90
commit 51d55ac231
18 changed files with 59 additions and 2 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -207,6 +207,7 @@ do_stations() {
gen_3d chip_golden 257 "a single thick cut golden fried potato chip, crisp browned edges"
# the egg bench
gen_3d egg_carton 259 "an open cardboard egg carton holding six brown eggs, lid folded back"
gen_3d egg_carton_empty 260 "an open EMPTY cardboard egg carton with six empty moulded hollows, lid folded back, no eggs in it at all"
gen_3d mixing_bowl 261 "a wide shallow white ceramic mixing bowl, empty, seen at a three quarter angle"
gen_3d tall_glass 263 "a plain tall clear drinking glass of water, empty of anything else"
# the poach bench

View File

@ -100,6 +100,11 @@ export class EggBenchView implements View {
carton.position.set(-1.7, BENCH_Y + 0.09, -0.9);
carton.receiveShadow = true;
this.root.add(carton);
// ponytail: NO carton prop here. The generated one ships with six eggs
// baked into the mesh, and this bench is entirely about which eggs are
// YOURS — draggable, float-tested, state-coloured. A carton that already
// has eggs in it is a lie the player has to see past. Wants an EMPTY tray;
// egg_carton_empty.glb is queued for it.
// The glass of water, right — the lie detector.
this.glass = new THREE.Mesh(

View File

@ -19,6 +19,7 @@ import {
} from '../sim/fryer';
import { el, Panel } from '../ui/hud';
import { loadBackdrop } from './props';
import { loadProp } from './assets';
const POT_Y = 0.55;
const POT_R = 1.05;
@ -107,11 +108,22 @@ export class FryerPotView implements View {
);
pot.position.y = POT_Y - 0.05;
this.root.add(pot);
// The oil surface stays procedural — it is the thermometer, and it has to
// shimmer, darken and be readable. Only the vessel gets replaced.
void loadProp('/assets/models/fryer_pot.glb', 2.05)
.then((prop) => {
prop.position.set(0, 0.04, 0);
pot.visible = false;
this.root.add(prop);
})
.catch(() => undefined);
// Sized to the GLB pot's inner rim, not the understudy cylinder's — the
// oil poking out past the steel was the giveaway.
this.oilSurf = new THREE.Mesh(
new THREE.CylinderGeometry(POT_R * 0.92, POT_R * 0.92, 0.05, 32),
new THREE.CylinderGeometry(POT_R * 0.64, POT_R * 0.64, 0.05, 32),
new THREE.MeshStandardMaterial({ color: 0xc9a23a, roughness: 0.15, metalness: 0.3, transparent: true, opacity: 0.9 }),
);
this.oilSurf.position.y = POT_Y + 0.12;
this.oilSurf.position.y = POT_Y + 0.2;
this.root.add(this.oilSurf);
// The basket: a wire cage on a long handle, chips inside.
@ -127,6 +139,16 @@ export class FryerPotView implements View {
handle.position.set(1.35, 0.18, 0);
this.basketGrp.add(cage, handle);
this.root.add(this.basketGrp);
void loadProp('/assets/models/fryer_basket.glb', 1.7)
.then((prop) => {
// Sits proud of the oil line: a basket you cannot see is a basket you
// cannot judge. The handle stays procedural — it reads as the thing
// your hand is on.
prop.position.set(0, -0.02, 0);
cage.visible = false;
this.basketGrp.add(prop);
})
.catch(() => undefined);
// The rack, stage left — where the resting happens.
this.rack = new THREE.Mesh(
@ -136,6 +158,13 @@ export class FryerPotView implements View {
this.rack.position.set(-2.5, 0.45, 0.6);
this.rack.receiveShadow = true;
this.root.add(this.rack);
void loadProp('/assets/models/drain_rack.glb', 1.8)
.then((prop) => {
prop.position.set(-2.5, 0.4, 0.6);
this.rack.visible = false;
this.root.add(prop);
})
.catch(() => undefined);
}
reset(count = 8, cardText = '', askText = 'chips — twice. once for cooked, once for golden'): void {

View File

@ -21,6 +21,7 @@ import {
} from '../sim/poach';
import { el, Panel } from '../ui/hud';
import { loadBackdrop } from './props';
import { loadProp } from './assets';
const POT_Y = 0.55;
const POT_R = 1.15;
@ -176,6 +177,19 @@ export class PoachView implements View {
this.spoon.add(bowl, handle);
this.spoon.visible = false;
this.root.add(this.spoon);
// The real spoon rides INSIDE the group, so it inherits every move the
// lift animation makes; the procedural bowl+handle step aside.
void loadProp('/assets/models/slotted_spoon.glb', 1.9)
.then((prop) => {
// Measured, not guessed: the model is ~1.9 long in Z with the bowl at
// the +Z end, so it needs shifting back by half its length to put the
// BOWL — not the model's centre — under the egg.
prop.position.set(0, -0.14, -0.92);
bowl.visible = false;
handle.visible = false;
this.spoon.add(prop);
})
.catch(() => undefined);
}
/** A fresh pot on `fuel`, cold water, no egg. */

View File

@ -24,6 +24,7 @@ import {
} from '../sim/pot';
import { el, Panel } from '../ui/hud';
import { loadBackdrop } from './props';
import { loadProp } from './assets';
const POT_Y = 0.55;
const POT_R = 1.2;
@ -107,6 +108,13 @@ export class PotView implements View {
);
pot.position.y = POT_Y - 0.05;
this.root.add(pot);
void loadProp('/assets/models/stock_pot.glb', 2.8)
.then((prop) => {
prop.position.set(0, 0.08, 0);
pot.visible = false;
this.root.add(prop);
})
.catch(() => undefined);
this.water = new THREE.Mesh(
new THREE.CylinderGeometry(POT_R * 0.93, POT_R * 0.93, 0.06, 32),
new THREE.MeshStandardMaterial({ color: 0x86aec2, roughness: 0.18, transparent: true, opacity: 0.8 }),