Skip to content

Commit f87efd7

Browse files
author
DavidQ
committed
Fix bezel path duplication and background draw order without resizing canvas
BUILD_PR_LEVEL_10_20_FIX_BEZEL_PATH_AND_BACKGROUND_DRAW_ORDER
1 parent 0929904 commit f87efd7

10 files changed

Lines changed: 161 additions & 60 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,50 @@ MODEL: GPT-5.4
22
REASONING: high
33

44
COMMAND:
5-
Create `BUILD_PR_LEVEL_10_19_REAL_IMPLEMENTATION_DELTA_FULLSCREEN_BEZEL_AND_BACKGROUND` as a real implementation PR, not a docs-only PR.
6-
7-
Assume these files already exist and are the correct conventions:
8-
- `games/Asteroids/assets/images/background.png`
9-
- `games/Asteroids/assets/images/bezel.png`
10-
11-
Implement all of the following with actual runtime code changes:
12-
13-
1. Add/use a dedicated `backgroundImage` class/module
14-
- autodiscover `games/<gameId>/assets/images/background.png`
15-
- canvas-rendered
16-
- draw immediately after clear
17-
- draw before all world/gameplay content
18-
- render ONLY during gameplay states
19-
- do NOT render in attract/title/select-player/menu/non-gameplay states
20-
21-
2. Add/use a dedicated `fullscreenBezel` class/module
22-
- autodiscover `games/<gameId>/assets/images/bezel.png`
23-
- HTML/container level overlay above canvas
24-
- only visible in fullscreen
25-
- must be visibly on screen, not DOM-only
26-
- verify/fix sizing, positioning, stacking context, host attachment, z-index, overflow, opacity, and fullscreen lifecycle wiring
27-
28-
3. Add focused tests/validation covering:
29-
- gameplay-only background gating
30-
- background draw order after clear and before world render
31-
- fullscreen bezel visibility on screen
32-
- bezel hidden outside fullscreen
33-
- no-op when files are missing
34-
35-
4. REQUIRED OUTPUT CONTENT
36-
The final ZIP MUST include actual changed implementation files.
37-
Docs-only output is not acceptable.
38-
39-
5. Final packaging step is REQUIRED
40-
Package ALL changed files into this exact repo-structured ZIP:
41-
`<project folder>/tmp/BUILD_PR_LEVEL_10_19_REAL_IMPLEMENTATION_DELTA_FULLSCREEN_BEZEL_AND_BACKGROUND.zip`
5+
Create `BUILD_PR_LEVEL_10_20_FIX_BEZEL_PATH_AND_BACKGROUND_DRAW_ORDER`.
6+
7+
Implement the confirmed Asteroids runtime fixes:
8+
9+
1. Fix fullscreen bezel path duplication
10+
- current bad URL example:
11+
`http://127.0.0.1:5500/games/Asteroids/games/Asteroids/assets/images/bezel.png`
12+
- normalize asset path resolution so bezel resolves once to:
13+
`games/Asteroids/assets/images/bezel.png`
14+
15+
2. Preserve canvas layout
16+
- do NOT resize the canvas
17+
- do NOT stretch the canvas to the viewport
18+
- keep canvas centered
19+
- preserve intended game W x H dimensions
20+
21+
3. Fix background rendering
22+
- keep `backgroundImage` as a separate class/module from `fullscreenBezel`
23+
- draw `backgroundImage` only during gameplay states
24+
- draw it immediately after clear
25+
- draw it before starfield and before all other world/gameplay content
26+
- inspect Asteroids render order and fix any layer that currently hides the background
27+
28+
4. Keep bezel as HTML overlay
29+
- keep `fullscreenBezel` as a separate HTML/container-level overlay
30+
- only show it in fullscreen
31+
- ensure it is visibly on screen, not just present in the DOM
32+
33+
5. Validate
34+
- bezel URL/path not duplicated
35+
- bezel visible in fullscreen
36+
- canvas size unchanged
37+
- canvas centered
38+
- background visible during gameplay
39+
- background absent in non-gameplay states
40+
- starfield no longer hides background incorrectly
41+
42+
6. Final packaging step is REQUIRED
43+
- package ALL changed files into this exact repo-structured ZIP:
44+
`<project folder>/tmp/BUILD_PR_LEVEL_10_20_FIX_BEZEL_PATH_AND_BACKGROUND_DRAW_ORDER.zip`
4245

4346
Hard rules:
44-
- do real implementation work
45-
- include changed source files in the ZIP
46-
- no docs-only completion
47+
- write implementation code only through Codex
48+
- include changed implementation files in Codex ZIP
49+
- no docs-only completion by Codex
4750
- no missing ZIP
4851
- no unrelated repo changes

docs/dev/COMMIT_COMMENT.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Implement real fullscreen bezel and gameplay background runtime delta
2-
BUILD_PR_LEVEL_10_19_REAL_IMPLEMENTATION_DELTA_FULLSCREEN_BEZEL_AND_BACKGROUND
1+
Fix bezel path duplication and background draw order without resizing canvas
2+
BUILD_PR_LEVEL_10_20_FIX_BEZEL_PATH_AND_BACKGROUND_DRAW_ORDER

docs/dev/NEXT_COMMAND.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
BUILD_PR_LEVEL_10_20_VERIFY_ASTEROIDS_FULLSCREEN_BEZEL_ON_SCREEN
1+
BUILD_PR_LEVEL_10_21_VERIFY_CENTERED_CANVAS_BEZEL_AND_GAMEPLAY_BACKGROUND
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
- Reframed the next PR to require actual implementation files in the ZIP
2-
- Locked asset assumptions to images/background.png and images/bezel.png
3-
- Kept separate classes: backgroundImage and fullscreenBezel
4-
- Enforced gameplay-only background and visible fullscreen bezel behavior
1+
- Reissued as strict docs-only BUILD_PR bundle
2+
- Codex instructed to fix bezel path duplication
3+
- Canvas must remain centered at original W x H
4+
- backgroundImage must render after clear and before starfield/world
5+
- backgroundImage limited to gameplay states only
Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
- ZIP contains changed implementation files, not docs only
2-
- Background renders only during gameplay
3-
- Background draws after clear and before world/gameplay
4-
- Bezel is visibly on screen in fullscreen
5-
- Bezel is hidden when not fullscreen
6-
- Missing background/bezel files do not break runtime
7-
- Output ZIP created at `<project folder>/tmp/BUILD_PR_LEVEL_10_19_REAL_IMPLEMENTATION_DELTA_FULLSCREEN_BEZEL_AND_BACKGROUND.zip`
1+
- Bundle is docs-only
2+
- Codex command requires implementation by Codex, not ChatGPT
3+
- Bezel URL/path must resolve once
4+
- Canvas must remain centered and unchanged in size
5+
- Background must draw only during gameplay
6+
- Background must draw before starfield/world content
7+
- Codex output ZIP path must be:
8+
<project folder>/tmp/BUILD_PR_LEVEL_10_20_FIX_BEZEL_PATH_AND_BACKGROUND_DRAW_ORDER.zip
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# BUILD_PR_LEVEL_10_20_FIX_BEZEL_PATH_AND_BACKGROUND_DRAW_ORDER
2+
3+
## Purpose
4+
Prepare a docs-only BUILD_PR bundle for Codex to implement the confirmed Asteroids runtime fixes.
5+
6+
## Confirmed issues
7+
1. Fullscreen bezel asset path is duplicated.
8+
Example bad URL:
9+
`http://127.0.0.1:5500/games/Asteroids/games/Asteroids/assets/images/bezel.png`
10+
11+
2. Canvas must remain centered and keep its intended W x H dimensions.
12+
13+
3. Background is still not visible and is likely being covered by starfield or another later draw pass.
14+
15+
4. Background should render only during gameplay, not attract, title, select-player, menu, or other non-gameplay states.
16+
17+
## Required implementation for Codex
18+
19+
### A. Fix fullscreen bezel path resolution
20+
- Resolve bezel asset path exactly once.
21+
- Use:
22+
`games/Asteroids/assets/images/bezel.png`
23+
- Do not duplicate the `games/Asteroids/` prefix.
24+
- Keep bezel as an HTML-level overlay above the canvas.
25+
26+
### B. Preserve canvas sizing and centering
27+
- Do not resize the canvas.
28+
- Do not stretch the canvas to the viewport.
29+
- Keep the game canvas centered.
30+
- Preserve intended game width and height.
31+
32+
### C. Background draw order and state gating
33+
- Keep `backgroundImage` separate from `fullscreenBezel`.
34+
- `backgroundImage` draws to the canvas.
35+
- Draw `backgroundImage` immediately after clear.
36+
- Draw `backgroundImage` before starfield and before all other world/gameplay layers.
37+
- Draw `backgroundImage` only during gameplay states.
38+
- Do not draw `backgroundImage` during attract/title/select-player/menu/non-gameplay states.
39+
40+
### D. Bezel behavior
41+
- Keep `fullscreenBezel` separate from canvas rendering.
42+
- Render `fullscreenBezel` at the HTML/container level.
43+
- Show bezel only while fullscreen is active.
44+
- Ensure bezel is visibly on screen, not just present in the DOM.
45+
46+
## Validation targets
47+
Codex must validate:
48+
- bezel URL/path is not duplicated
49+
- bezel is visible in fullscreen
50+
- canvas dimensions are unchanged
51+
- canvas remains centered
52+
- background is visible during gameplay
53+
- background does not render in non-gameplay states
54+
- starfield no longer hides background by draw order mistake
55+
56+
## Packaging requirement
57+
Codex must package all changed files into:
58+
`<project folder>/tmp/BUILD_PR_LEVEL_10_20_FIX_BEZEL_PATH_AND_BACKGROUND_DRAW_ORDER.zip`
59+
60+
## Scope guard
61+
- Docs-first PR bundle only
62+
- Codex writes implementation code
63+
- No unrelated repo changes

games/Asteroids/game/AsteroidsGameScene.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,9 @@ export default class AsteroidsGameScene extends Scene {
690690
render(renderer) {
691691
const leaderboardTopScore = this.highScoreService.getTopScore(this.highScoreRows);
692692
const liveHudHighScore = Math.max(this.session.highScore, leaderboardTopScore);
693-
renderer.drawRect(0, 0, this.world.bounds.width, this.world.bounds.height, '#020617');
693+
if (this.session.mode !== 'playing') {
694+
renderer.drawRect(0, 0, this.world.bounds.width, this.world.bounds.height, '#020617');
695+
}
694696
this.world.starfield.forEach((star) => {
695697
renderer.drawRect(star.x, star.y, star.size, star.size, '#94a3b8');
696698
});

src/engine/runtime/backgroundImage.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { resolveGameImageConventionPaths } from "./gameImageConvention.js";
1+
import { resolveGameImageConventionPaths, resolveRuntimeAssetUrl } from "./gameImageConvention.js";
22

33
const NON_GAMEPLAY_MODE_TOKENS = Object.freeze([
44
"menu",
@@ -76,9 +76,10 @@ function sceneModeCandidates(scene) {
7676

7777
export default class backgroundImage {
7878
constructor(options = {}) {
79+
this.documentRef = options.documentRef || globalThis.document || null;
7980
const resolved = resolveGameImageConventionPaths({
8081
gameId: options.gameId,
81-
documentRef: options.documentRef || globalThis.document || null
82+
documentRef: this.documentRef
8283
});
8384
this.gameId = resolved.gameId;
8485
this.layer = createLayerState(resolved.backgroundPath);
@@ -156,7 +157,7 @@ export default class backgroundImage {
156157
};
157158

158159
try {
159-
image.src = this.layer.path;
160+
image.src = resolveRuntimeAssetUrl(this.layer.path, this.documentRef);
160161
} catch {
161162
this.layer.status = "missing";
162163
}

src/engine/runtime/fullscreenBezel.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { resolveGameImageConventionPaths } from "./gameImageConvention.js";
1+
import { resolveGameImageConventionPaths, resolveRuntimeAssetUrl } from "./gameImageConvention.js";
22

33
function toDisplayValue(visible) {
44
return visible ? "block" : "none";
@@ -184,7 +184,7 @@ export default class fullscreenBezel {
184184
this.missing = true;
185185
element.style.display = "none";
186186
};
187-
element.src = this.path;
187+
element.src = resolveRuntimeAssetUrl(this.path, this.documentRef);
188188

189189
this.host.appendChild?.(element);
190190
this.element = element;

src/engine/runtime/gameImageConvention.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ function normalizePath(value) {
66
return safeText(value, "").replace(/\\/g, "/");
77
}
88

9+
function hasUrlProtocol(value) {
10+
return /^[a-z][a-z0-9+.-]*:/i.test(value);
11+
}
12+
913
function discoverGameIdFromDocument(documentRef) {
1014
const pathname = normalizePath(documentRef?.location?.pathname || "");
1115
if (!pathname) {
@@ -23,6 +27,32 @@ function toImagePath(gameId, fileName) {
2327
return `games/${id}/assets/images/${fileName}`;
2428
}
2529

30+
export function resolveRuntimeAssetUrl(pathValue, documentRef = null) {
31+
const normalized = normalizePath(pathValue);
32+
if (!normalized) {
33+
return "";
34+
}
35+
36+
if (hasUrlProtocol(normalized) || normalized.startsWith("//")) {
37+
return normalized;
38+
}
39+
40+
if (normalized.startsWith("/")) {
41+
return normalized;
42+
}
43+
44+
if (normalized.startsWith("./") || normalized.startsWith("../")) {
45+
try {
46+
const baseHref = safeText(documentRef?.location?.href, "http://localhost/");
47+
return new URL(normalized, baseHref).pathname;
48+
} catch {
49+
return `/${normalized.replace(/^\/+/, "")}`;
50+
}
51+
}
52+
53+
return `/${normalized.replace(/^\/+/, "")}`;
54+
}
55+
2656
export function resolveGameImageConventionPaths(options = {}) {
2757
const gameId = safeText(options.gameId, "") || discoverGameIdFromDocument(options.documentRef || null);
2858
return {

0 commit comments

Comments
 (0)