Skip to content

Commit a99525f

Browse files
author
DavidQ
committed
Level 19.2 gameplay overlay integration.
Enable overlays during gameplay runtime without interference.
1 parent be2ceeb commit a99525f

12 files changed

Lines changed: 383 additions & 25 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ MODEL: GPT-5.4-codex
22
REASONING: medium
33

44
COMMAND:
5-
Implement overlay expansion framework:
6-
- Define extension interfaces/contracts
7-
- Ensure compatibility with config-driven overlays
8-
- Do not change existing behavior
5+
Integrate overlay system into gameplay runtime:
6+
- Allow overlays to run during gameplay
7+
- Ensure no interference with controls/rendering
8+
- Preserve debug overlay behavior
99

1010
Package ZIP to <project folder>/tmp/

docs/dev/COMMIT_COMMENT.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Level 19.1 overlay system expansion framework.
2-
Introduce extension points for future overlay types.
1+
Level 19.2 gameplay overlay integration.
2+
Enable overlays during gameplay runtime without interference.

docs/dev/PROJECT_INSTRUCTIONS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Commit Comment:
9999
- Always proceed to the next logical step
100100

101101
### NO COMMIT-ONLY PRs
102-
- PRs must include something testable
102+
- PRs must include something testable and improve the Roadmap
103103
- If a PR is doc-only, bundle with next smallest executable/testable change
104104

105105
### ZIP IS ALWAYS REQUIRED
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# BUILD_PR_LEVEL_19_2_OVERLAY_GAMEPLAY_RUNTIME_INTEGRATION Report
2+
3+
## Purpose
4+
Integrate overlay extension support into gameplay runtime without changing existing debug overlay behavior.
5+
6+
## Scope Applied
7+
- Add optional runtime overlay extension hooks (`onStep`, `onRender`) to overlay contracts/config.
8+
- Add a shared gameplay runtime runner for overlay extension hooks.
9+
- Wire gameplay sample runtimes to execute overlay hooks safely (non-blocking).
10+
- Preserve existing input, rendering, and debug overlay behavior by default.
11+
12+
## Files
13+
- `samples/phase-17/shared/overlayExpansionContracts.js`
14+
- `samples/phase-17/shared/overlayGameplayRuntime.js`
15+
- `samples/phase-17/shared/overlayStackBySampleConfig.js`
16+
- `samples/phase-17/1708/RealGameplayMiniGameScene.js`
17+
- `samples/phase-17/1710/RealGameplayMiniGameScene.js`
18+
- `tests/runtime/Phase17OverlayExpansionFramework.test.mjs`
19+
- `tests/runtime/Phase17OverlayGameplayRuntimeIntegration.test.mjs`
20+
21+
## Behavior Notes
22+
- Existing overlays keep the same stack order, cycle keys, persistence, and placement.
23+
- Runtime hooks are opt-in; current configs use empty runtime hook arrays.
24+
- Hook failures are contained and do not break gameplay step/render execution.
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[ ] Existing overlays load
2-
[ ] Extension points defined
3-
[ ] No regression
4-
[ ] Config compatibility maintained
1+
[ ] Gameplay runs normally
2+
[ ] Overlays visible in gameplay
3+
[ ] No input conflicts
4+
[ ] No render issues

docs/pr/BUILD_PR.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
1-
# BUILD_PR_LEVEL_19_1_OVERLAY_SYSTEM_EXPANSION_FRAMEWORK
1+
# BUILD_PR_LEVEL_19_2_GAMEPLAY_OVERLAY_INTEGRATION
22

33
## PLAN
44

55
### Purpose
6-
Establish a framework for expanding the overlay system beyond debug use into reusable gameplay and tool overlays.
6+
Integrate overlay system into gameplay layer so overlays can be used during active gameplay, not just debug/testing.
77

88
### Goals
9-
- Define extension points for new overlays
10-
- Ensure compatibility with existing config-driven system
11-
- Maintain strict separation between debug and gameplay overlays
9+
- Enable overlays in gameplay runtime
10+
- Maintain separation from debug overlays
11+
- Ensure no gameplay interference
1212

1313
---
1414

1515
## BUILD
1616

1717
### Scope
18-
- Define overlay extension interface/contracts
19-
- Document integration pattern for new overlays
20-
- Ensure existing overlays conform to framework
21-
- No behavior change
18+
- Hook overlay system into gameplay runtime
19+
- Allow gameplay-safe overlays
20+
- Ensure overlays do not block input or rendering
21+
- No change to debug overlays
2222

2323
### Test Steps
24-
1. Validate existing overlays still load
25-
2. Confirm extension points available
26-
3. Verify no regression
24+
1. Run gameplay sample
25+
2. Enable overlays
26+
3. Verify gameplay unaffected
27+
4. Cycle overlays
2728

2829
### Expected
29-
- Overlay system ready for expansion
30-
- No impact to existing behavior
30+
- Overlays work during gameplay
31+
- No input or render conflicts

samples/phase-17/1708/RealGameplayMiniGameScene.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ import {
3838
OVERLAY_UI_LAYER,
3939
} from '/samples/phase-17/shared/miniGameOverlayStack.js';
4040
import { getRequiredLevel17OverlayStackConfig } from '/samples/phase-17/shared/overlayStackBySampleConfig.js';
41+
import {
42+
createOverlayGameplayRuntime,
43+
renderOverlayGameplayRuntime,
44+
setOverlayGameplayRuntimeExtensions,
45+
stepOverlayGameplayRuntime,
46+
} from '/samples/phase-17/shared/overlayGameplayRuntime.js';
4147

4248
const theme = new Theme(ThemeTokens);
4349

@@ -105,6 +111,9 @@ export default class RealGameplayMiniGameScene extends Scene {
105111
setTabDebugOverlayCycleKey(this.tabDebugOverlays, DEBUG_OVERLAY_CONFIG.cycleKey || MINI_GAME_DEBUG_CYCLE_KEY);
106112
setTabDebugOverlayPersistenceKey(this.tabDebugOverlays, DEBUG_OVERLAY_CONFIG.persistenceKey);
107113
this.setDebugOverlayCycleMap(DEBUG_OVERLAY_CONFIG.overlays, DEBUG_OVERLAY_CONFIG.initialOverlayId);
114+
this.overlayGameplayRuntime = createOverlayGameplayRuntime({
115+
runtimeExtensions: DEBUG_OVERLAY_CONFIG.runtimeExtensions,
116+
});
108117
}
109118

110119
addEvent(text) {
@@ -202,6 +211,10 @@ export default class RealGameplayMiniGameScene extends Scene {
202211
setTabDebugOverlayPersistenceKey(this.tabDebugOverlays, persistenceKey);
203212
}
204213

214+
setOverlayGameplayRuntimeExtensions(runtimeExtensions) {
215+
return setOverlayGameplayRuntimeExtensions(this.overlayGameplayRuntime, runtimeExtensions);
216+
}
217+
205218
isDebugOverlayActive(overlayId) {
206219
return isTabDebugOverlayActive(this.tabDebugOverlays, overlayId);
207220
}
@@ -473,6 +486,14 @@ export default class RealGameplayMiniGameScene extends Scene {
473486
this.lastCollisionCount = this.debugCollisionRows.length;
474487
this.updateFeedback(dt);
475488
this.refreshMissionFeedState();
489+
stepOverlayGameplayRuntime(this.overlayGameplayRuntime, {
490+
scene: this,
491+
engine,
492+
input,
493+
dtSeconds: dt,
494+
gameState: this.gameState,
495+
activeOverlayId: this.getActiveDebugOverlayId(),
496+
});
476497
this.syncCamera();
477498
}
478499

@@ -621,5 +642,11 @@ export default class RealGameplayMiniGameScene extends Scene {
621642
`Camera mode: ${this.viewState.cameraMode}`,
622643
]);
623644
}
645+
renderOverlayGameplayRuntime(this.overlayGameplayRuntime, {
646+
scene: this,
647+
renderer,
648+
gameState: this.gameState,
649+
activeOverlayId,
650+
});
624651
}
625652
}

samples/phase-17/1710/RealGameplayMiniGameScene.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ import {
3838
OVERLAY_UI_LAYER,
3939
} from '/samples/phase-17/shared/miniGameOverlayStack.js';
4040
import { getRequiredLevel17OverlayStackConfig } from '/samples/phase-17/shared/overlayStackBySampleConfig.js';
41+
import {
42+
createOverlayGameplayRuntime,
43+
renderOverlayGameplayRuntime,
44+
setOverlayGameplayRuntimeExtensions,
45+
stepOverlayGameplayRuntime,
46+
} from '/samples/phase-17/shared/overlayGameplayRuntime.js';
4147

4248
const theme = new Theme(ThemeTokens);
4349

@@ -105,6 +111,9 @@ export default class RealGameplayMiniGameScene extends Scene {
105111
setTabDebugOverlayCycleKey(this.tabDebugOverlays, DEBUG_OVERLAY_CONFIG.cycleKey || MINI_GAME_DEBUG_CYCLE_KEY);
106112
setTabDebugOverlayPersistenceKey(this.tabDebugOverlays, DEBUG_OVERLAY_CONFIG.persistenceKey);
107113
this.setDebugOverlayCycleMap(DEBUG_OVERLAY_CONFIG.overlays, DEBUG_OVERLAY_CONFIG.initialOverlayId);
114+
this.overlayGameplayRuntime = createOverlayGameplayRuntime({
115+
runtimeExtensions: DEBUG_OVERLAY_CONFIG.runtimeExtensions,
116+
});
108117
}
109118

110119
addEvent(text) {
@@ -202,6 +211,10 @@ export default class RealGameplayMiniGameScene extends Scene {
202211
setTabDebugOverlayPersistenceKey(this.tabDebugOverlays, persistenceKey);
203212
}
204213

214+
setOverlayGameplayRuntimeExtensions(runtimeExtensions) {
215+
return setOverlayGameplayRuntimeExtensions(this.overlayGameplayRuntime, runtimeExtensions);
216+
}
217+
205218
isDebugOverlayActive(overlayId) {
206219
return isTabDebugOverlayActive(this.tabDebugOverlays, overlayId);
207220
}
@@ -473,6 +486,14 @@ export default class RealGameplayMiniGameScene extends Scene {
473486
this.lastCollisionCount = this.debugCollisionRows.length;
474487
this.updateFeedback(dt);
475488
this.refreshMissionFeedState();
489+
stepOverlayGameplayRuntime(this.overlayGameplayRuntime, {
490+
scene: this,
491+
engine,
492+
input,
493+
dtSeconds: dt,
494+
gameState: this.gameState,
495+
activeOverlayId: this.getActiveDebugOverlayId(),
496+
});
476497
this.syncCamera();
477498
}
478499

@@ -621,5 +642,11 @@ export default class RealGameplayMiniGameScene extends Scene {
621642
`Camera mode: ${this.viewState.cameraMode}`,
622643
]);
623644
}
645+
renderOverlayGameplayRuntime(this.overlayGameplayRuntime, {
646+
scene: this,
647+
renderer,
648+
gameState: this.gameState,
649+
activeOverlayId,
650+
});
624651
}
625652
}

samples/phase-17/shared/overlayExpansionContracts.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,49 @@ function resolveInitialOverlayId(overlays, initialOverlayId) {
4545
return exists ? requestedId : overlays[0].id;
4646
}
4747

48+
function normalizeRuntimeExtensionEntry(entry) {
49+
if (!entry || typeof entry !== 'object') {
50+
return null;
51+
}
52+
53+
const overlayId = String(entry.overlayId || '').trim();
54+
const onStep = typeof entry.onStep === 'function' ? entry.onStep : null;
55+
const onRender = typeof entry.onRender === 'function' ? entry.onRender : null;
56+
if (!onStep && !onRender) {
57+
return null;
58+
}
59+
60+
return Object.freeze({
61+
overlayId,
62+
onStep,
63+
onRender,
64+
});
65+
}
66+
67+
function normalizeRuntimeExtensions(runtimeExtensions) {
68+
if (!Array.isArray(runtimeExtensions) || runtimeExtensions.length === 0) {
69+
return Object.freeze([]);
70+
}
71+
72+
const normalized = [];
73+
for (let i = 0; i < runtimeExtensions.length; i += 1) {
74+
const candidate = normalizeRuntimeExtensionEntry(runtimeExtensions[i]);
75+
if (!candidate) {
76+
continue;
77+
}
78+
normalized.push(candidate);
79+
}
80+
return Object.freeze(normalized);
81+
}
82+
4883
export function defineOverlayExtensionContract({
4984
id = '',
5085
overlays = [],
5186
initialOverlayId = '',
5287
cycleKey = '',
5388
persistenceKey = '',
5489
channel = DEFAULT_CHANNEL,
90+
runtimeExtensions = [],
5591
metadata = {},
5692
} = {}) {
5793
const normalizedId = String(id || '').trim();
@@ -68,6 +104,7 @@ export function defineOverlayExtensionContract({
68104
const normalizedPersistenceKey = String(persistenceKey || '').trim();
69105
const normalizedChannel = String(channel || DEFAULT_CHANNEL).trim() || DEFAULT_CHANNEL;
70106
const normalizedInitialOverlayId = resolveInitialOverlayId(normalizedOverlays, initialOverlayId);
107+
const normalizedRuntimeExtensions = normalizeRuntimeExtensions(runtimeExtensions);
71108

72109
return Object.freeze({
73110
id: normalizedId,
@@ -76,6 +113,7 @@ export function defineOverlayExtensionContract({
76113
initialOverlayId: normalizedInitialOverlayId,
77114
cycleKey: normalizedCycleKey,
78115
persistenceKey: normalizedPersistenceKey,
116+
runtimeExtensions: normalizedRuntimeExtensions,
79117
metadata: Object.freeze({ ...(metadata || {}) }),
80118
});
81119
}
@@ -105,5 +143,6 @@ export function getOverlayControllerConfigFromContract(contract) {
105143
initialOverlayId: contract.initialOverlayId,
106144
cycleKey: contract.cycleKey,
107145
persistenceKey: contract.persistenceKey,
146+
runtimeExtensions: contract.runtimeExtensions,
108147
});
109148
}

0 commit comments

Comments
 (0)