-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix: Missing binds in settings #3963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -210,6 +210,8 @@ export class InputHandler { | |
| private selectionBoxActive: boolean = false; | ||
| // True while warships are selected via box (waiting for move target click) | ||
| private multiSelectionActive: boolean = false; | ||
| // True while the configured shiftKey (box-select hold key) is held down | ||
| private shiftKeybindHeld: boolean = false; | ||
|
|
||
| // Touch long-press state | ||
| private longPressTimer: ReturnType<typeof setTimeout> | null = null; | ||
|
|
@@ -307,8 +309,8 @@ export class InputHandler { | |
| let deltaX = 0; | ||
| let deltaY = 0; | ||
|
|
||
| // Skip if shift is held down | ||
| if (this.activeKeys.has(this.keybinds.shiftKey)) { | ||
| // Skip if box-select key is held down | ||
| if (this.shiftKeybindHeld) { | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -433,15 +435,15 @@ export class InputHandler { | |
| this.keybinds.centerCamera, | ||
| "ControlLeft", | ||
| "ControlRight", | ||
| this.keybinds.shiftKey, | ||
| ].includes(e.code) | ||
| ) { | ||
| this.activeKeys.add(e.code); | ||
| } | ||
|
|
||
| // Shift = warship box selection mode. | ||
| // If a ghost structure is active, discard it first. | ||
| if (e.code === this.keybinds.shiftKey) { | ||
| if (this.keybindMatchesEvent(e, this.keybinds.shiftKey)) { | ||
| this.shiftKeybindHeld = true; | ||
| if (this.uiState.ghostStructure !== null) { | ||
| this.setGhostStructure(null); | ||
| } | ||
|
|
@@ -478,8 +480,7 @@ export class InputHandler { | |
| this.eventBus.emit(new AlternateViewEvent(false)); | ||
| } | ||
|
|
||
| const resetKey = this.keybinds.resetGfx ?? "KeyR"; | ||
| if (e.code === resetKey && this.isAltKeyHeld(e)) { | ||
| if (this.keybindMatchesEvent(e, this.keybinds.resetGfx ?? "Alt+KeyR")) { | ||
| e.preventDefault(); | ||
| this.eventBus.emit(new RefreshGraphicsEvent()); | ||
| } | ||
|
|
@@ -516,7 +517,7 @@ export class InputHandler { | |
| this.eventBus.emit(new CenterCameraEvent()); | ||
| } | ||
|
|
||
| if (e.code === this.keybinds.selectAllWarships) { | ||
| if (this.keybindMatchesEvent(e, this.keybinds.selectAllWarships)) { | ||
| e.preventDefault(); | ||
| this.eventBus.emit(new SelectAllWarshipsEvent()); | ||
| } | ||
|
|
@@ -571,13 +572,13 @@ export class InputHandler { | |
|
|
||
| this.activeKeys.delete(e.code); | ||
|
|
||
| // Reset crosshair when Shift is released (unless selection box or multi-selection still active) | ||
| if ( | ||
| e.code === this.keybinds.shiftKey && | ||
| !this.selectionBoxActive && | ||
| !this.multiSelectionActive | ||
| ) { | ||
| this.canvas.style.cursor = ""; | ||
| // Reset crosshair when box-select key is released (unless selection box or multi-selection still active) | ||
| const parsedShiftKey = this.parseKeybind(this.keybinds.shiftKey); | ||
| if (e.code === parsedShiftKey.code) { | ||
| this.shiftKeybindHeld = false; | ||
| if (!this.selectionBoxActive && !this.multiSelectionActive) { | ||
| this.canvas.style.cursor = ""; | ||
| } | ||
| } | ||
|
Comment on lines
+575
to
582
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Release logic should also handle modifier-first keyup for combo binds. This reset checks only 🤖 Prompt for AI Agents |
||
| }); | ||
| } | ||
|
|
@@ -688,6 +689,11 @@ export class InputHandler { | |
| } | ||
| } | ||
|
|
||
| if (this.dispatchPointerKeybindActions(event)) { | ||
| this.suppressNextTap = false; | ||
| return; | ||
| } | ||
|
|
||
| if (this.isModifierKeyPressed(event)) { | ||
| this.suppressNextTap = false; | ||
| this.eventBus.emit(new ShowBuildMenuEvent(event.clientX, event.clientY)); | ||
|
|
@@ -800,7 +806,7 @@ export class InputHandler { | |
| // started, continue emitting selection box updates | ||
| if ( | ||
| this.selectionBoxActive || | ||
| this.activeKeys.has(this.keybinds.shiftKey) || | ||
| this.shiftKeybindHeld || | ||
| this.longPressActive | ||
| ) { | ||
| this.selectionBoxActive = true; | ||
|
|
@@ -849,24 +855,133 @@ export class InputHandler { | |
| } | ||
|
|
||
| /** | ||
| * Parses a keybind value that may include a "Shift+" prefix. | ||
| * e.g. "Shift+KeyB" → { shift: true, code: "KeyB" } | ||
| * "KeyB" → { shift: false, code: "KeyB" } | ||
| * Parses a keybind value that may include a "Shift+" or "Alt+" prefix. | ||
| * e.g. "Shift+KeyB" → { shift: true, alt: false, code: "KeyB" } | ||
| * "Alt+KeyR" → { shift: false, alt: true, code: "KeyR" } | ||
| * "KeyB" → { shift: false, alt: false, code: "KeyB" } | ||
| */ | ||
| private parseKeybind(value: string): { shift: boolean; code: string } { | ||
| private parseKeybind(value: string): { | ||
| shift: boolean; | ||
| alt: boolean; | ||
| code: string; | ||
| } { | ||
| if (value?.startsWith("Shift+")) { | ||
| return { shift: true, code: value.slice(6) }; | ||
| return { shift: true, alt: false, code: value.slice(6) }; | ||
| } | ||
| if (value?.startsWith("Alt+")) { | ||
| return { shift: false, alt: true, code: value.slice(4) }; | ||
| } | ||
| return { shift: false, code: value }; | ||
| return { shift: false, alt: false, code: value }; | ||
| } | ||
|
|
||
| private static readonly MODIFIER_KEY_CODES = new Set([ | ||
| "ShiftLeft", | ||
| "ShiftRight", | ||
| "AltLeft", | ||
| "AltRight", | ||
| "ControlLeft", | ||
| "ControlRight", | ||
| "MetaLeft", | ||
| "MetaRight", | ||
| ]); | ||
|
|
||
| /** | ||
| * Returns true if the keyboard event matches the given keybind value, | ||
| * including optional Shift+ prefix support. | ||
| * including optional Shift+ and Alt+ prefix support. | ||
| */ | ||
| private keybindMatchesEvent(e: KeyboardEvent, keybindValue: string): boolean { | ||
| const parsed = this.parseKeybind(keybindValue); | ||
| return e.code === parsed.code && e.shiftKey === parsed.shift; | ||
| if (e.code !== parsed.code) return false; | ||
| // A bare modifier-key bind (e.g. "ShiftLeft") can't be matched on flag | ||
| // state: pressing the key itself sets its own modifier flag to true. | ||
| if ( | ||
| !parsed.shift && | ||
| !parsed.alt && | ||
| InputHandler.MODIFIER_KEY_CODES.has(parsed.code) | ||
| ) { | ||
| return true; | ||
| } | ||
| return e.shiftKey === parsed.shift && e.altKey === parsed.alt; | ||
| } | ||
|
|
||
| private static readonly MOUSE_BUTTON_NAMES: Record<number, string> = { | ||
| 0: "MouseLeft", | ||
| 1: "MouseMiddle", | ||
| 2: "MouseRight", | ||
| }; | ||
|
Comment on lines
+907
to
+911
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Search for tests covering MouseRight keybind behavior
rg -n 'MouseRight.*keybind|keybind.*MouseRight' --type=ts
# Check if MouseRight is exposed in UI defaults
rg -n 'MouseRight' src/core/game/UserSettings.ts src/client/UserSettingModal.tsRepository: openfrontio/OpenFrontIO Length of output: 49 🏁 Script executed: # Find all usages of MOUSE_BUTTON_NAMES
rg -n 'MOUSE_BUTTON_NAMES' src/client/ --type=ts
# Check how keybind actions are initialized and filtered
rg -n 'keybind.*action|action.*keybind' src/client/InputHandler.ts --type=ts -A 3
# Look for context menu handling that might conflict with MouseRight
rg -n 'contextmenu|context.menu|rightclick|right.click' src/client/ --type=ts -iRepository: openfrontio/OpenFrontIO Length of output: 3796 🏁 Script executed: # Find keybindMatchesPointerEvent implementation
rg -n 'keybindMatchesPointerEvent' src/client/InputHandler.ts -A 20
# Check if there's filtering of button 2 in keybind logic
rg -n 'e\.button|button.*2|MouseRight' src/client/InputHandler.ts -B 2 -A 2
# Look for keybind defaults/configuration
rg -n 'keybind.*default|default.*keybind' src/core/game/UserSettings.ts -A 3Repository: openfrontio/OpenFrontIO Length of output: 2031 🏁 Script executed: # Check keybind defaults in UserSettings
rg -n 'keybinds|Keybind' src/core/game/UserSettings.ts -A 2 -B 2
# Look for any MouseRight in default keybinds
rg -n 'MouseRight' src/core/game/ --type=ts
# Check what keybinds are actually bound by default
rg -n 'boatAttack|boatMove' src/core/game/UserSettings.tsRepository: openfrontio/OpenFrontIO Length of output: 1566 🏁 Script executed: # Get the full getDefaultKeybinds function
rg -n 'export function getDefaultKeybinds' src/core/game/UserSettings.ts -A 50
# Check if any default keybinds use mouse buttons
rg -n 'MouseLeft|MouseMiddle|MouseRight' src/core/game/UserSettings.tsRepository: openfrontio/OpenFrontIO Length of output: 1731 Remove MouseRight from keybind support to match established constraints. The Remove the MouseRight entry from 🤖 Prompt for AI Agents |
||
|
|
||
| private keybindMatchesPointerEvent( | ||
| e: PointerEvent, | ||
| keybindValue: string, | ||
| ): boolean { | ||
| if (!keybindValue) return false; | ||
| const parsed = this.parseKeybind(keybindValue); | ||
| const buttonName = InputHandler.MOUSE_BUTTON_NAMES[e.button]; | ||
| return ( | ||
| parsed.code === buttonName && | ||
| e.shiftKey === parsed.shift && | ||
| e.altKey === parsed.alt | ||
| ); | ||
| } | ||
|
|
||
| private dispatchPointerKeybindActions(event: PointerEvent): boolean { | ||
| const actions: Array<[string, () => void]> = [ | ||
| [ | ||
| this.keybinds.boatAttack, | ||
| () => this.eventBus.emit(new DoBoatAttackEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.groundAttack, | ||
| () => this.eventBus.emit(new DoGroundAttackEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.retaliateAttack, | ||
| () => this.eventBus.emit(new DoRetaliateAttackEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.requestAlliance, | ||
| () => this.eventBus.emit(new DoRequestAllianceEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.breakAlliance, | ||
| () => this.eventBus.emit(new DoBreakAllianceEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.swapDirection, | ||
| () => this.eventBus.emit(new SwapRocketDirectionEvent(true)), | ||
| ], | ||
| [ | ||
| this.keybinds.selectAllWarships, | ||
| () => this.eventBus.emit(new SelectAllWarshipsEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.centerCamera, | ||
| () => this.eventBus.emit(new CenterCameraEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.pauseGame, | ||
| () => this.eventBus.emit(new TogglePauseIntentEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.gameSpeedUp, | ||
| () => this.eventBus.emit(new GameSpeedUpIntentEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.gameSpeedDown, | ||
| () => this.eventBus.emit(new GameSpeedDownIntentEvent()), | ||
| ], | ||
| [ | ||
| this.keybinds.resetGfx ?? "Alt+KeyR", | ||
| () => this.eventBus.emit(new RefreshGraphicsEvent()), | ||
| ], | ||
| ]; | ||
| for (const [keybind, action] of actions) { | ||
| if (this.keybindMatchesPointerEvent(event, keybind)) { | ||
| action(); | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
Comment on lines
+927
to
985
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix Line 939 hardcodes Proposed fix [
this.keybinds.swapDirection,
- () => this.eventBus.emit(new SwapRocketDirectionEvent(true)),
+ () => {
+ const nextDirection = !this.uiState.rocketDirectionUp;
+ this.eventBus.emit(new SwapRocketDirectionEvent(nextDirection));
+ },
],🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,7 +91,6 @@ export class UserSettingModal extends BaseModal { | |
| activeKeybinds[k] = normalizedValue; | ||
| } | ||
| } | ||
|
|
||
| const values = Object.entries(activeKeybinds) | ||
| .filter(([k]) => k !== action) | ||
| .map(([, v]) => v); | ||
|
|
@@ -401,6 +400,16 @@ export class UserSettingModal extends BaseModal { | |
| @change=${this.handleKeybindChange} | ||
| ></setting-keybind> | ||
|
|
||
| <setting-keybind | ||
| action="resetGfx" | ||
| label=${translateText("user_setting.reset_gfx")} | ||
| description=${translateText("user_setting.reset_gfx_desc")} | ||
| .defaultKey=${this.defaultKeybinds.resetGfx} | ||
| .value=${this.getKeyValue("resetGfx")} | ||
| .display=${this.getKeyChar("resetGfx")} | ||
| @change=${this.handleKeybindChange} | ||
| ></setting-keybind> | ||
|
|
||
| <h2 | ||
| class="text-blue-200 text-xl font-bold mt-8 mb-3 border-b border-white/10 pb-2" | ||
| > | ||
|
|
@@ -639,6 +648,26 @@ export class UserSettingModal extends BaseModal { | |
| @change=${this.handleKeybindChange} | ||
| ></setting-keybind> | ||
|
|
||
| <setting-keybind | ||
| action="selectAllWarships" | ||
| label=${translateText("user_setting.select_all_warships")} | ||
| description=${translateText("user_setting.select_all_warships_desc")} | ||
| .defaultKey=${this.defaultKeybinds.selectAllWarships} | ||
| .value=${this.getKeyValue("selectAllWarships")} | ||
| .display=${this.getKeyChar("selectAllWarships")} | ||
| @change=${this.handleKeybindChange} | ||
| ></setting-keybind> | ||
|
|
||
| <setting-keybind | ||
| action="shiftKey" | ||
| label=${translateText("user_setting.shift_key")} | ||
| description=${translateText("user_setting.shift_key_desc")} | ||
| .defaultKey=${this.defaultKeybinds.shiftKey} | ||
| .value=${this.getKeyValue("shiftKey")} | ||
| .display=${this.getKeyChar("shiftKey")} | ||
| @change=${this.handleKeybindChange} | ||
| ></setting-keybind> | ||
|
Comment on lines
+651
to
+669
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New warship keybind settings can save combos that gameplay may not execute. These new controls accept modifier combos, but runtime matching for these actions is still not fully modifier-aware. Users can configure valid-looking binds that won’t trigger. 🤖 Prompt for AI Agents |
||
|
|
||
| <h2 | ||
| class="text-blue-200 text-xl font-bold mt-8 mb-3 border-b border-white/10 pb-2" | ||
| > | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reset
shiftKeybindHeldon blur to prevent stuck input mode.If the window blurs while the hold key is down, this flag can stay
trueand keep pan-drag blocked after focus returns. Clear it in the existingblurcleanup block with the other transient input state.Proposed fix
window.addEventListener("blur", () => { this.activeKeys.clear(); + this.shiftKeybindHeld = false; if (this.alternateView) { this.alternateView = false; this.eventBus.emit(new AlternateViewEvent(false)); }🤖 Prompt for AI Agents