not-tonight/src/scenes/floor/overlays/PourOverlay.ts
2026-07-21 02:17:45 +10:00

102 lines
3.3 KiB
TypeScript

import Phaser from 'phaser';
import { POUR_SWEEP_MS, POUR_CLEAN_FROM, POUR_OVER_FROM } from '../barShift';
import { POUR_UI } from '../../../data/strings/floor';
const W = 640;
const H = 360;
const GLASS_H = 120;
const GLASS_W = 44;
/**
* The pour (docs/SCENARIOS.md, bartender shift): a marker sweeps 0→1→0 and
* SPACE stops it — the fill IS your pour. The clean band sits near the top
* because a good pour is nearly a mistake. Same shell discipline as the other
* overlays: the scene routes keys in and calls update(dt).
*/
export class PourOverlay {
private root: Phaser.GameObjects.Container | null = null;
private fillRect: Phaser.GameObjects.Rectangle | null = null;
private t = 0;
private rising = true;
private done: ((fill: number | null) => void) | null = null;
constructor(private readonly scene: Phaser.Scene) {}
get isOpen(): boolean {
return this.root !== null;
}
open(done: (fill: number | null) => void): void {
if (this.root) return;
this.done = done;
this.t = 0;
this.rising = true;
const root = this.scene.add.container(0, 0).setDepth(900).setScrollFactor(0);
this.root = root;
root.add(this.scene.add.rectangle(W / 2, H / 2, W, H, 0x03030a, 0.72));
root.add(this.scene.add.rectangle(W / 2, H / 2, 280, 200, 0x10121c).setStrokeStyle(1, 0x3a4054));
root.add(
this.scene.add
.text(W / 2, H / 2 - 82, POUR_UI.title, { fontFamily: 'monospace', fontSize: '13px', color: '#e8e4da' })
.setOrigin(0.5),
);
// The glass, and the bands painted on its side like an honest jug.
const gx = W / 2;
const gBottom = H / 2 + 58;
root.add(this.scene.add.rectangle(gx, gBottom - GLASS_H / 2, GLASS_W + 8, GLASS_H + 6, 0x232838));
const band = (from: number, to: number, colour: number): void => {
const y0 = gBottom - GLASS_H * from;
const y1 = gBottom - GLASS_H * to;
root.add(
this.scene.add
.rectangle(gx + GLASS_W / 2 + 10, (y0 + y1) / 2, 4, y0 - y1, colour)
.setOrigin(0.5),
);
};
band(0, POUR_CLEAN_FROM, 0x5a4a48); // short: the region of regret
band(POUR_CLEAN_FROM, POUR_OVER_FROM, 0x74a06a); // clean
band(POUR_OVER_FROM, 1, 0xd8a020); // overflow: beloved, expensive
this.fillRect = this.scene.add
.rectangle(gx, gBottom, GLASS_W, 1, 0xc8a24a)
.setOrigin(0.5, 1);
root.add(this.fillRect);
root.add(
this.scene.add
.text(W / 2, H / 2 + 78, POUR_UI.help, { fontFamily: 'monospace', fontSize: '9px', color: '#8890a8' })
.setOrigin(0.5),
);
}
handleKey(key: string): void {
if (!this.root) return;
if (key === 'ESCAPE' || key === 'ESC') return this.finish(null);
if (key === 'SPACE') return this.finish(this.t);
}
update(dtMs: number): void {
if (!this.root) return;
const step = dtMs / POUR_SWEEP_MS;
this.t += this.rising ? step : -step;
if (this.t >= 1) {
this.t = 1;
this.rising = false;
} else if (this.t <= 0) {
this.t = 0;
this.rising = true;
}
this.fillRect?.setDisplaySize(GLASS_W, Math.max(1, GLASS_H * this.t));
}
private finish(fill: number | null): void {
const cb = this.done;
this.root?.destroy();
this.root = null;
this.fillRect = null;
this.done = null;
cb?.(fill);
}
}