Hide the load behind the roster board
~130 sprites meant several seconds of blank screen after picking a shift. RosterScene now warms the prop textures while you read the cards — opportunistic and non-blocking, so clicking through early is no worse than before. Manifest via plain fetch to avoid the nested load.once(COMPLETE) pattern. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8bd219796a
commit
442efa4c3c
@ -2432,3 +2432,36 @@ paperwork. Tweens here decorate; `update()` owns the state machine.
|
||||
The desk handed off to NightSummary while **staying active underneath it** —
|
||||
nobody stopped it. Same class as the dev-route bug that left a live Night
|
||||
driving a dead Door. `go()` now stops itself before calling `next()`.
|
||||
|
||||
## SESSION — FABLE-SOLO-27 · 2026-07-22
|
||||
|
||||
**Branch:** main (solo, Fable — sole operator from here) · **Gate:** lint ✓ build ✓ test ✓ (831 tests, 57 files)
|
||||
|
||||
### The blank screen after picking a shift
|
||||
The art batch is ~130 sprites now and NightScene loads every one of them before
|
||||
it launches Door and Floor. Cold, that is several seconds of BLANK SCREEN after
|
||||
you pick a card, with nothing to say the game had not died. (It also fooled me
|
||||
mid-verification into calling a regression on main that was never there — the
|
||||
loader was simply still working, progress climbing 0.25 → 0.5 → 0.76 → 1.)
|
||||
|
||||
Fix: **RosterScene prewarms the prop textures while you read the board.** You
|
||||
are looking at three shift cards anyway; by the time you pick one the room is
|
||||
already loaded and Door/Floor come up at once. A quiet line under the board
|
||||
reads `unlocking the doors · 64%` → `the room is ready`.
|
||||
|
||||
- Opportunistic, never blocking: the cards are live immediately, and clicking
|
||||
through early just leaves NightScene to load the rest exactly as before. It
|
||||
is never worse than it was, only usually better.
|
||||
- The manifest is fetched with plain `fetch()`, not the Phaser loader, so there
|
||||
is no `load.once(COMPLETE)` registered from inside another COMPLETE handler —
|
||||
that nested pattern is what NightScene already has to live with, and it is
|
||||
the kind of thing that works until an asset count changes.
|
||||
- Textures are game-global, so anything warmed here counts for the night.
|
||||
- No manifest at all stays a supported state (placeholders, silence).
|
||||
|
||||
### Git hygiene check (asked for, and worth recording)
|
||||
Working tree clean · HEAD == origin/main exactly (0 ahead, 0 behind) · no
|
||||
stashes · two dangling commits are pre-rebase copies of my own work, and every
|
||||
file in them verified byte-identical on HEAD or differing only where the other
|
||||
session also edited · all 14 feature markers from this session's work confirmed
|
||||
present · deployed bundle hash matches the local build.
|
||||
|
||||
@ -14,6 +14,10 @@ const H = 360;
|
||||
const MONO = 'monospace';
|
||||
const NIGHT_NAMES = ['thursday', 'friday', 'saturday'] as const;
|
||||
|
||||
/** Quiet, because it is not the player's problem — just proof of life. */
|
||||
const PREWARM = 'unlocking the doors · {pct}%';
|
||||
const PREWARM_DONE = 'the room is ready';
|
||||
|
||||
const CARD_W = 168;
|
||||
const CARD_H = 150;
|
||||
|
||||
@ -25,6 +29,7 @@ interface PassThrough {
|
||||
|
||||
export class RosterScene extends Phaser.Scene {
|
||||
private carried: PassThrough = {};
|
||||
private prewarm!: Phaser.GameObjects.Text;
|
||||
|
||||
constructor() {
|
||||
super('Roster');
|
||||
@ -34,6 +39,44 @@ export class RosterScene extends Phaser.Scene {
|
||||
this.carried = data ?? {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Warm the prop textures while the player reads the board.
|
||||
*
|
||||
* The art batch is ~130 sprites now, and NightScene loads them all before it
|
||||
* launches Door and Floor — which meant several seconds of BLANK SCREEN after
|
||||
* you picked a shift, with nothing to say the game hadn't died. The roster is
|
||||
* the natural place to hide that: you are reading three cards anyway.
|
||||
*
|
||||
* Opportunistic, never blocking. The cards are live immediately; click
|
||||
* through early and NightScene loads whatever is left exactly as before.
|
||||
* Textures are game-global, so anything that lands here counts there.
|
||||
*/
|
||||
private prewarmProps(): void {
|
||||
// Plain fetch rather than the Phaser loader for the manifest: chaining a
|
||||
// second `load.once(COMPLETE)` from inside the first COMPLETE handler is
|
||||
// the fragile pattern NightScene already has to live with. One queue, one
|
||||
// start, no nesting.
|
||||
void fetch('props/manifest.json')
|
||||
.then((r) => (r.ok ? r.json() : null))
|
||||
.then((m: { props?: string[] } | null) => {
|
||||
if (!m || !this.scene.isActive()) return;
|
||||
const missing = (m.props ?? []).filter((k) => !this.textures.exists(`gen:prop:${k}`));
|
||||
if (missing.length === 0) return;
|
||||
for (const k of missing) this.load.image(`gen:prop:${k}`, `props/${k}.png`);
|
||||
this.load.on(Phaser.Loader.Events.PROGRESS, (v: number) => {
|
||||
if (this.prewarm.active) this.prewarm.setText(PREWARM.replace('{pct}', String(Math.round(v * 100))));
|
||||
});
|
||||
this.load.once(Phaser.Loader.Events.COMPLETE, () => {
|
||||
if (this.prewarm.active) this.prewarm.setText(PREWARM_DONE);
|
||||
});
|
||||
this.load.start();
|
||||
})
|
||||
.catch(() => {
|
||||
// No manifest at all is a supported state — the game renders
|
||||
// placeholders and says nothing about it. Same here.
|
||||
});
|
||||
}
|
||||
|
||||
create(): void {
|
||||
// Display only — NightScene remains the authority on run state (it will
|
||||
// clearSave on freshRun itself; we just show the board that run implies).
|
||||
@ -95,5 +138,10 @@ export class RosterScene extends Phaser.Scene {
|
||||
fontFamily: MONO, fontSize: '7px', color: '#6a5a44',
|
||||
})
|
||||
.setOrigin(0.5);
|
||||
|
||||
this.prewarm = this.add
|
||||
.text(W / 2, H - 32, '', { fontFamily: MONO, fontSize: '7px', color: '#5a4c38' })
|
||||
.setOrigin(0.5);
|
||||
this.prewarmProps();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user