Skip to content

Commit 44e06a9

Browse files
author
DavidQ
committed
Add overlay focus and input priority rules.
PR Details: - Defines gameplay-first input priority - Prevents overlay focus conflicts
1 parent e86560a commit 44e06a9

9 files changed

Lines changed: 107 additions & 62 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@ MODEL: GPT-5.4
22
REASONING: medium
33

44
COMMAND:
5-
Create BUILD_PR_LEVEL_19_3_OVERLAY_INTERACTION_CONTROLS as the next smallest executable/testable PR.
5+
Implement overlay focus and input priority rules:
6+
- Gameplay input remains primary
7+
- Overlay controls scoped to explicit actions
8+
- No focus stealing
9+
- No Tab usage
610

7-
Requirements:
8-
- Add gameplay-safe interaction controls for overlays introduced by Level 19.2
9-
- Support show/hide and cycle behavior in gameplay runtime
10-
- Reuse the shared overlay/input mapping; do not reintroduce hardcoded Tab behavior
11-
- Preserve existing debug overlay behavior
12-
- Add or update focused tests proving overlay controls do not interfere with gameplay controls
13-
- Keep scope narrow and testable
14-
- Do not modify start_of_day folders
15-
- Package the repo-structured ZIP to <project folder>/tmp/BUILD_PR_LEVEL_19_3_OVERLAY_INTERACTION_CONTROLS.zip
11+
Package ZIP to <project folder>/tmp/

docs/dev/COMMIT_COMMENT.txt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
Add gameplay-safe overlay interaction controls.
1+
Add overlay focus and input priority rules.
22

33
PR Details:
4-
- Enables show/hide/cycle controls for gameplay overlays
5-
- Preserves shared non-Tab input mapping
6-
- Adds focused validation for non-interference with gameplay controls
4+
- Defines gameplay-first input priority
5+
- Prevents overlay focus conflicts

docs/dev/PROJECT_INSTRUCTIONS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ Commit Comment:
121121
- Preserve wording unless the PR explicitly requires rewriting.
122122
- Keep roadmap handling status-only unless explicitly requested otherwise.
123123
- Do not delete roadmap content during cleanup work.
124+
- Do not modify roadmap content during cleanup work.
125+
- Only update status [ ] [.] [x] in roadmap content during cleanup work.
126+
124127

125128
## EXECUTION EFFICIENCY
126129

@@ -132,4 +135,6 @@ Commit Comment:
132135
- Choose the correct path automatically
133136
- Reduce the number of options presented
134137
- Do the right thing and complete the task fully and correctly
138+
- Don't ask if I want the next bundled PR, assume I want it.
139+
- Update Roadmap stutus every PR.
135140

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# BUILD_PR_LEVEL_19_4_OVERLAY_FOCUS_AND_INPUT_PRIORITY Report
2+
3+
## Purpose
4+
Enforce gameplay-first input priority and explicit overlay runtime interaction controls with no focus stealing.
5+
6+
## Scope Applied
7+
- Overlay runtime controls are now explicit-action only:
8+
- show/hide: `Ctrl+G`
9+
- runtime-cycle: `Ctrl+Shift+G`
10+
- Plain debug cycle input (`G` / `Shift+G`) remains unchanged for debug overlays.
11+
- Gameplay input remains primary; overlay runtime controls do not block movement/control flow.
12+
- No `Tab` usage introduced.
13+
14+
## Files Changed
15+
- `samples/phase-17/shared/overlayCycleInput.js`
16+
- `samples/phase-17/shared/overlayGameplayRuntime.js`
17+
- `tests/runtime/Phase17OverlayGameplayRuntimeIntegration.test.mjs`
18+
19+
## Validation
20+
- `tests/runtime/Phase17OverlayGameplayRuntimeIntegration.test.mjs` PASS
21+
- `tests/runtime/Phase17RealGameplaySample.test.mjs` PASS
22+
- `tests/runtime/Phase17Sample1712GameplayMetricsTelemetry.test.mjs` PASS
23+
- `tests/runtime/Phase17Sample1713FinalReferenceGame.test.mjs` PASS
24+
- `tests/runtime/Phase17TabDebugOverlayCycle1707Plus.test.mjs` PASS
25+
- `tests/runtime/Phase17DebugOverlayBottomRightPosition.test.mjs` PASS
26+
- `tests/runtime/Phase18RuntimeLayerScaffold.test.mjs` PASS
27+
- `tests/runtime/Phase18IntegrationFlowPass.test.mjs` PASS
28+
- `tests/runtime/Phase18CoreServicesSkeleton.test.mjs` PASS
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
[ ] Gameplay sample with overlays loads
2-
[ ] Overlay show/hide control works
3-
[ ] Overlay cycle control works
4-
[ ] Gameplay controls remain responsive while using overlay controls
5-
[ ] No Tab-based overlay interaction is introduced
6-
[ ] Debug overlay behavior remains unchanged
1+
[ ] Gameplay input unaffected
2+
[ ] Overlay controls work
3+
[ ] No focus conflicts
4+
[ ] No Tab usage

docs/pr/BUILD_PR.md

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,19 @@
1-
# BUILD_PR_LEVEL_19_3_OVERLAY_INTERACTION_CONTROLS
1+
# BUILD_PR_LEVEL_19_4_OVERLAY_FOCUS_AND_INPUT_PRIORITY
22

33
## Purpose
4-
Add testable interaction controls for gameplay-safe overlays introduced in Level 19.2 so overlays can be shown, hidden, and cycled without interfering with gameplay input.
4+
Define and enforce focus + input priority rules so gameplay and overlays coexist without conflicts.
55

66
## Scope
7-
- Define gameplay-safe overlay interaction controls
8-
- Keep debug-overlay behavior intact
9-
- Ensure overlay interaction does not steal or block primary gameplay controls
10-
- Validate control behavior in at least one gameplay-active sample
11-
12-
## Included
13-
- Interaction control contract for gameplay overlays
14-
- Runtime hookup for overlay show/hide/cycle actions in gameplay context
15-
- Tests covering non-interference with gameplay controls
16-
- Validation notes for control behavior
17-
18-
## Excluded
19-
- New overlay types
20-
- Overlay visual redesign
21-
- Mission or telemetry feature expansion
22-
- Repo-wide input cleanup
23-
24-
## Execution Notes
25-
- Use the shared input mapping established by prior overlay/input consolidation work
26-
- Preserve current non-Tab overlay cycle behavior
27-
- Keep scope to the smallest executable/testable change
28-
- Do not modify start_of_day folders
7+
- Establish input priority: gameplay > overlay (except explicit overlay controls)
8+
- Define focus model (no hard focus steal by overlays)
9+
- Ensure overlay controls are scoped and non-invasive
2910

3011
## Test Steps
31-
1. Load a gameplay-active sample with overlays enabled
32-
2. Trigger overlay show/hide control
33-
3. Trigger overlay cycle control
34-
4. Confirm gameplay movement/actions still work while overlay controls are used
35-
5. Confirm debug overlay behavior remains unchanged where applicable
12+
1. Run gameplay sample
13+
2. Trigger overlay controls
14+
3. Verify gameplay input remains primary
15+
4. Confirm overlay controls only act when invoked
3616

37-
## Expected Result
38-
- Gameplay overlays can be interacted with safely
39-
- Overlay controls do not interfere with core gameplay input
40-
- Existing debug overlay behavior remains stable
17+
## Expected
18+
- No input conflicts
19+
- Predictable priority behavior

samples/phase-17/shared/overlayCycleInput.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ overlayCycleInput.js
77
export const LEVEL17_OVERLAY_CYCLE_KEY = 'KeyG';
88
export const LEVEL17_OVERLAY_REVERSE_MODIFIERS = Object.freeze(['ShiftLeft', 'ShiftRight']);
99
export const LEVEL19_OVERLAY_RUNTIME_TOGGLE_MODIFIERS = Object.freeze(['ControlLeft', 'ControlRight']);
10+
export const LEVEL19_OVERLAY_RUNTIME_CYCLE_MODIFIERS = LEVEL17_OVERLAY_REVERSE_MODIFIERS;
1011
export const LEVEL17_OVERLAY_CYCLE_LABEL = 'G/Shift+G';
1112

1213
export function isOverlayCycleReverseModifierActive(input) {
@@ -37,3 +38,20 @@ export function isOverlayRuntimeToggleModifierActive(input) {
3738
export function getOverlayRuntimeToggleInputCodes() {
3839
return [LEVEL17_OVERLAY_CYCLE_KEY, LEVEL19_OVERLAY_RUNTIME_TOGGLE_MODIFIERS[0]];
3940
}
41+
42+
export function isOverlayRuntimeCycleModifierActive(input) {
43+
for (let i = 0; i < LEVEL19_OVERLAY_RUNTIME_CYCLE_MODIFIERS.length; i += 1) {
44+
if (input?.isDown(LEVEL19_OVERLAY_RUNTIME_CYCLE_MODIFIERS[i]) === true) {
45+
return true;
46+
}
47+
}
48+
return false;
49+
}
50+
51+
export function getOverlayRuntimeCycleInputCodes() {
52+
return [
53+
LEVEL17_OVERLAY_CYCLE_KEY,
54+
LEVEL19_OVERLAY_RUNTIME_TOGGLE_MODIFIERS[0],
55+
LEVEL19_OVERLAY_RUNTIME_CYCLE_MODIFIERS[0],
56+
];
57+
}

samples/phase-17/shared/overlayGameplayRuntime.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ David Quesenberry
55
overlayGameplayRuntime.js
66
*/
77
import {
8-
isOverlayCycleReverseModifierActive,
8+
isOverlayRuntimeCycleModifierActive,
99
isOverlayRuntimeToggleModifierActive,
1010
LEVEL17_OVERLAY_CYCLE_KEY,
1111
} from '/samples/phase-17/shared/overlayCycleInput.js';
@@ -123,11 +123,15 @@ export function stepOverlayGameplayRuntimeControls(runtime, input) {
123123
const cycleKey = String(runtime.interactionCycleKey || LEVEL17_OVERLAY_CYCLE_KEY);
124124
const cyclePressed = input?.isDown(cycleKey) === true;
125125
const toggleModifierActive = isOverlayRuntimeToggleModifierActive(input);
126-
const togglePressed = cyclePressed && toggleModifierActive;
127-
const reverseActive = isOverlayCycleReverseModifierActive(input);
126+
const cycleModifierActive = isOverlayRuntimeCycleModifierActive(input);
127+
const explicitActionPressed = cyclePressed && toggleModifierActive;
128+
const togglePressed = explicitActionPressed && !cycleModifierActive;
129+
const runtimeCyclePressed = explicitActionPressed && cycleModifierActive;
128130

129-
if (!togglePressed) {
131+
if (!explicitActionPressed) {
130132
runtime.interactionToggleLatch = false;
133+
runtime.interactionCycleLatch = false;
134+
return false;
131135
}
132136

133137
if (togglePressed && runtime.interactionToggleLatch === false) {
@@ -137,8 +141,10 @@ export function stepOverlayGameplayRuntimeControls(runtime, input) {
137141
return true;
138142
}
139143

140-
if (!cyclePressed || toggleModifierActive) {
141-
runtime.interactionCycleLatch = false;
144+
if (!runtimeCyclePressed) {
145+
if (!togglePressed) {
146+
runtime.interactionCycleLatch = false;
147+
}
142148
return false;
143149
}
144150

@@ -153,8 +159,7 @@ export function stepOverlayGameplayRuntimeControls(runtime, input) {
153159

154160
normalizeInteractionIndex(runtime);
155161
const count = runtime.runtimeExtensions.length;
156-
const delta = reverseActive ? -1 : 1;
157-
runtime.interactionIndex = (runtime.interactionIndex + delta + count) % count;
162+
runtime.interactionIndex = (runtime.interactionIndex + 1 + count) % count;
158163
return true;
159164
}
160165

tests/runtime/Phase17OverlayGameplayRuntimeIntegration.test.mjs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import assert from 'node:assert/strict';
88
import RealGameplayMiniGameScene from '../../samples/phase-17/1708/RealGameplayMiniGameScene.js';
99
import {
1010
getOverlayCycleInputCodes,
11+
getOverlayRuntimeCycleInputCodes,
1112
getOverlayRuntimeToggleInputCodes,
1213
} from '../../samples/phase-17/shared/overlayCycleInput.js';
1314
import { resetTabDebugOverlayPersistenceForTests } from '../../samples/phase-17/shared/tabDebugOverlayCycle.js';
@@ -71,7 +72,15 @@ function pressOverlayRuntimeToggle(scene) {
7172
scene.step3DPhysics(0.02, { input: makeInput([]) });
7273
}
7374

75+
function pressOverlayRuntimeCycle(scene) {
76+
scene.step3DPhysics(0.02, { input: makeInput(getOverlayRuntimeCycleInputCodes()) });
77+
scene.step3DPhysics(0.02, { input: makeInput([]) });
78+
}
79+
7480
function assertGameplayOverlayRuntimeHooksAreNonBlocking() {
81+
assert.equal(getOverlayRuntimeToggleInputCodes().includes('Tab'), false, 'Runtime overlay toggle mapping must not use Tab.');
82+
assert.equal(getOverlayRuntimeCycleInputCodes().includes('Tab'), false, 'Runtime overlay cycle mapping must not use Tab.');
83+
7584
const scene = new RealGameplayMiniGameScene();
7685
scene.setCamera3D(createCameraStub());
7786

@@ -123,7 +132,11 @@ function assertGameplayOverlayRuntimeHooksAreNonBlocking() {
123132
scene.render(missionFeedRenderer);
124133
assert.equal(missionFeedRenderer.texts.some((text) => text.includes('Mission Feed')), true, 'Debug overlay cycle behavior should remain unchanged.');
125134
scene.step3DPhysics(0.05, { input: makeInput([]) });
126-
assert.equal(counters.bStep > beforeCycleBStep, true, 'Gameplay overlay runtime cycle control should switch active runtime extension.');
135+
assert.equal(counters.bStep, beforeCycleBStep, 'Plain debug overlay cycle should not switch gameplay runtime overlay extension.');
136+
137+
pressOverlayRuntimeCycle(scene);
138+
scene.step3DPhysics(0.05, { input: makeInput([]) });
139+
assert.equal(counters.bStep > beforeCycleBStep, true, 'Explicit runtime overlay cycle control should switch active runtime extension.');
127140

128141
const beforeHideStep = counters.aStep + counters.bStep;
129142
pressOverlayRuntimeToggle(scene);
@@ -134,6 +147,10 @@ function assertGameplayOverlayRuntimeHooksAreNonBlocking() {
134147
scene.step3DPhysics(0.05, { input: makeInput([]) });
135148
assert.equal(counters.aStep + counters.bStep > beforeHideStep, true, 'Runtime overlay toggle should restore runtime overlay execution.');
136149

150+
const beforeMoveWithRuntimeControl = scene.player.x;
151+
scene.step3DPhysics(0.2, { input: makeInput(['KeyD', ...getOverlayRuntimeCycleInputCodes()]) });
152+
assert.equal(scene.player.x > beforeMoveWithRuntimeControl, true, 'Gameplay movement input should remain primary during explicit runtime overlay control actions.');
153+
137154
scene.setOverlayGameplayRuntimeExtensions([
138155
{
139156
overlayId: 'mission-feed',

0 commit comments

Comments
 (0)