From a95feb223ef4a8df1ad243a5850bd97ebfe5cd73 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Tue, 10 Feb 2026 16:28:54 +0100 Subject: [PATCH] feat(nds): improve key input system --- app/components/Achievements/BottomScreen.vue | 2 +- app/components/Common/Buttons.vue | 2 +- .../Intro/BottomScreen/BottomScreen.vue | 2 +- app/components/NDS.vue | 35 ++---------- .../Projects/BottomScreen/Buttons.vue | 2 +- .../BottomScreen/LinkConfirmationPopup.vue | 4 +- .../BottomScreen/Menus/Clock/Achievements.vue | 2 +- .../BottomScreen/Menus/Options/2048.vue | 2 +- .../BottomScreen/Menus/User/Color.vue | 2 +- .../BottomScreen/Menus/User/Snake.vue | 2 +- .../Settings/BottomScreen/NumberInput.vue | 2 +- app/composables/useButtonNavigation.ts | 2 +- app/composables/useKeyDown.ts | 15 ++++- app/composables/useKeyUp.ts | 13 ++++- app/pages/index.vue | 57 ------------------- app/utils/input.ts | 17 ++++++ 16 files changed, 59 insertions(+), 102 deletions(-) create mode 100644 app/utils/input.ts diff --git a/app/components/Achievements/BottomScreen.vue b/app/components/Achievements/BottomScreen.vue index 04668ce..97f8626 100644 --- a/app/components/Achievements/BottomScreen.vue +++ b/app/components/Achievements/BottomScreen.vue @@ -8,7 +8,7 @@ const QUIT_SIZE = assets.images.achievements.quit.rect.width; const QUIT_X = Math.floor(LOGICAL_WIDTH / 2 - QUIT_SIZE / 2); const QUIT_Y = 135; -useKeyDown((key) => { +useKeyDown(({ key }) => { if (store.isIntro || store.isOutro) return; switch (key) { diff --git a/app/components/Common/Buttons.vue b/app/components/Common/Buttons.vue index f3d6e30..6f6d431 100644 --- a/app/components/Common/Buttons.vue +++ b/app/components/Common/Buttons.vue @@ -110,7 +110,7 @@ onClick((x, y) => { } }); -useKeyDown((key) => { +useKeyDown(({ key }) => { if (props.yOffset !== 0) return; switch (key) { case "NDS_START": diff --git a/app/components/Intro/BottomScreen/BottomScreen.vue b/app/components/Intro/BottomScreen/BottomScreen.vue index ea4c8b0..3380b5a 100644 --- a/app/components/Intro/BottomScreen/BottomScreen.vue +++ b/app/components/Intro/BottomScreen/BottomScreen.vue @@ -78,7 +78,7 @@ onClick(() => { store.animateOutro(); }); -useKeyDown((key) => { +useKeyDown(({ key }) => { if (store.isIntro || store.isOutro) return; switch (key) { diff --git a/app/components/NDS.vue b/app/components/NDS.vue index b8dc137..8d06992 100644 --- a/app/components/NDS.vue +++ b/app/components/NDS.vue @@ -203,38 +203,15 @@ const { onRender } = useLoop(); const physicalButtonsDown = new Set(); let mousePressedButton: string | null = null; -const keyToButton: Record = { - ArrowUp: "UP", - ArrowDown: "DOWN", - ArrowLeft: "LEFT", - ArrowRight: "RIGHT", - d: "A", - s: "B", - w: "X", - a: "Y", - D: "A", - S: "B", - W: "X", - A: "Y", - " ": "SELECT", - Enter: "START", -}; - -useKeyDown((key) => { - const button = keyToButton[key]; - if (button) { - physicalButtonsDown.add(button); - window.dispatchEvent( - new KeyboardEvent("keydown", { key: `NDS_${button}` }), - ); +useKeyDown(({ ndsButton }) => { + if (ndsButton) { + physicalButtonsDown.add(ndsButton); } }); -useKeyUp((key) => { - const button = keyToButton[key]; - if (button) { - physicalButtonsDown.delete(button); - window.dispatchEvent(new KeyboardEvent("keyup", { key: `NDS_${button}` })); +useKeyUp(({ ndsButton }) => { + if (ndsButton) { + physicalButtonsDown.delete(ndsButton); } }); diff --git a/app/components/Projects/BottomScreen/Buttons.vue b/app/components/Projects/BottomScreen/Buttons.vue index 06c83c4..00b91fd 100644 --- a/app/components/Projects/BottomScreen/Buttons.vue +++ b/app/components/Projects/BottomScreen/Buttons.vue @@ -162,7 +162,7 @@ onRender((ctx) => { ctx.fillStyle = `rgba(0, 0, 0, ${store.isIntro ? store.intro.fadeOpacity : store.isOutro ? store.outro.fadeOpacity : 0})`; ctx.fillRect(0, 0, LOGICAL_WIDTH, LOGICAL_HEIGHT); }); -useKeyDown((key) => { +useKeyDown(({ key }) => { if ( currentAnimation || store.isIntro || diff --git a/app/components/Projects/BottomScreen/LinkConfirmationPopup.vue b/app/components/Projects/BottomScreen/LinkConfirmationPopup.vue index 88e2902..b3b5f0a 100644 --- a/app/components/Projects/BottomScreen/LinkConfirmationPopup.vue +++ b/app/components/Projects/BottomScreen/LinkConfirmationPopup.vue @@ -40,7 +40,7 @@ onMounted(() => { onUnmounted(() => timeline?.kill()); -useKeyDown((key) => { +useKeyDown(({ key }) => { if (!store.showConfirmationPopup) return; if (textProgress.value < 1 && key === "NDS_A") { @@ -73,7 +73,7 @@ useKeyDown((key) => { } }); -useKeyUp((key) => { +useKeyUp(({ key }) => { if (store.showConfirmationPopup && key === "NDS_A") { waitingForNdsARelease = false; } diff --git a/app/components/Settings/BottomScreen/Menus/Clock/Achievements.vue b/app/components/Settings/BottomScreen/Menus/Clock/Achievements.vue index 0ceac9f..ff48fe8 100644 --- a/app/components/Settings/BottomScreen/Menus/Clock/Achievements.vue +++ b/app/components/Settings/BottomScreen/Menus/Clock/Achievements.vue @@ -131,7 +131,7 @@ onClick((x, y) => { } }); -useKeyDown((key) => { +useKeyDown(({ key }) => { if (isAnimating.value) return; if (key === "NDS_X") { handleVisitAll(); diff --git a/app/components/Settings/BottomScreen/Menus/Options/2048.vue b/app/components/Settings/BottomScreen/Menus/Options/2048.vue index 5f0dc4d..56e16e9 100644 --- a/app/components/Settings/BottomScreen/Menus/Options/2048.vue +++ b/app/components/Settings/BottomScreen/Menus/Options/2048.vue @@ -538,7 +538,7 @@ const slide = (rowDir: number, colDir: number) => { } }; -useKeyDown((key) => { +useKeyDown(({ key }) => { if (isAnimating.value) return; switch (key) { // TODO: remove this, testing only diff --git a/app/components/Settings/BottomScreen/Menus/User/Color.vue b/app/components/Settings/BottomScreen/Menus/User/Color.vue index 70153c7..6ef8ee8 100644 --- a/app/components/Settings/BottomScreen/Menus/User/Color.vue +++ b/app/components/Settings/BottomScreen/Menus/User/Color.vue @@ -139,7 +139,7 @@ const select = (col: number, row: number) => { app.setColor(col, row); }; -useKeyDown((key) => { +useKeyDown(({ key }) => { if (isAnimating.value) return; switch (key) { diff --git a/app/components/Settings/BottomScreen/Menus/User/Snake.vue b/app/components/Settings/BottomScreen/Menus/User/Snake.vue index 8b022ea..9fea84e 100644 --- a/app/components/Settings/BottomScreen/Menus/User/Snake.vue +++ b/app/components/Settings/BottomScreen/Menus/User/Snake.vue @@ -302,7 +302,7 @@ onRender((ctx) => { ); }, 110); -useKeyDown((key) => { +useKeyDown(({ key }) => { if (state.value !== "alive") return; const newDirection = direction.clone(); diff --git a/app/components/Settings/BottomScreen/NumberInput.vue b/app/components/Settings/BottomScreen/NumberInput.vue index 0bc4864..e96d6fb 100644 --- a/app/components/Settings/BottomScreen/NumberInput.vue +++ b/app/components/Settings/BottomScreen/NumberInput.vue @@ -212,7 +212,7 @@ onRender((ctx) => { ); }, 10); -useKeyDown((key) => { +useKeyDown(({ key }) => { if (isAnimating.value || !props.selected || props.disabled) return; switch (key) { case "NDS_UP": diff --git a/app/composables/useButtonNavigation.ts b/app/composables/useButtonNavigation.ts index bfcb7df..0e84bc8 100644 --- a/app/composables/useButtonNavigation.ts +++ b/app/composables/useButtonNavigation.ts @@ -221,7 +221,7 @@ export const useButtonNavigation = >({ } }); - useKeyDown((key) => { + useKeyDown(({ key }) => { if (blockInteractions.value) return; const currentButton = selectedButton.value as Entry; diff --git a/app/composables/useKeyDown.ts b/app/composables/useKeyDown.ts index cd400b1..8fb22e5 100644 --- a/app/composables/useKeyDown.ts +++ b/app/composables/useKeyDown.ts @@ -1,8 +1,19 @@ -export type KeyDownCallback = (key: string) => void; +import { mapKeyToNDS } from "~/utils/input"; + +export type KeyDownCallback = (params: { + key: string; + ndsButton: string | null; + repeated: boolean; +}) => void; export const useKeyDown = (callback: KeyDownCallback) => { const handleKeyDown = (event: KeyboardEvent) => { - callback(event.key); + const ndsButton = mapKeyToNDS(event.key); + callback({ + key: ndsButton ? `NDS_${ndsButton}` : event.key, + ndsButton, + repeated: event.repeat, + }); }; onMounted(() => { diff --git a/app/composables/useKeyUp.ts b/app/composables/useKeyUp.ts index ed78e9a..4c916ad 100644 --- a/app/composables/useKeyUp.ts +++ b/app/composables/useKeyUp.ts @@ -1,8 +1,17 @@ -export type KeyUpCallback = (key: string) => void; +import { mapKeyToNDS } from "~/utils/input"; + +export type KeyUpCallback = (params: { + key: string; + ndsButton: string | null; +}) => void; export const useKeyUp = (callback: KeyUpCallback) => { const handleKeyUp = (event: KeyboardEvent) => { - callback(event.key); + const ndsButton = mapKeyToNDS(event.key); + callback({ + key: ndsButton ? `NDS_${ndsButton}` : event.key, + ndsButton, + }); }; onMounted(() => { diff --git a/app/pages/index.vue b/app/pages/index.vue index fb4ad9a..7138292 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -12,63 +12,6 @@ const bottomScreen = useTemplateRef("bottomScreen"); const topScreenCanvas = computed(() => topScreen.value?.canvas ?? null); const bottomScreenCanvas = computed(() => bottomScreen.value?.canvas ?? null); - -const a = useAchievementsStore(); - -const keyToButton: Record = { - ArrowUp: "UP", - ArrowDown: "DOWN", - ArrowLeft: "LEFT", - ArrowRight: "RIGHT", - d: "A", - s: "B", - z: "X", - q: "Y", - D: "A", - S: "B", - Z: "X", - Q: "Y", - " ": "SELECT", - Enter: "START", -}; - -// events are dispatched from NDS.vue in 3d mode -// events are dispatched from here in 2d mode -// that's a bit dirty but who cares, there is a lot of dirty things going on here -// like who choose Nuxt to build such an app -useKeyDown((key) => { - if (app.settings.renderingMode === "3d") return; - const button = keyToButton[key]; - if (button) { - window.dispatchEvent( - new KeyboardEvent("keydown", { key: `NDS_${button}` }), - ); - } - - // testing purpose only - if (key === "m") { - a.reset(); - } else if (key === "o") { - for (const ach of ACHIEVEMENTS) { - a.unlock(ach.id); - } - } else if (key === "p") { - for (const ach of ACHIEVEMENTS) { - if (!a.isUnlocked(ach.id)) { - a.unlock(ach.id); - break; - } - } - } -}); - -useKeyUp((key) => { - if (app.settings.renderingMode === "3d") return; - const button = keyToButton[key]; - if (button) { - window.dispatchEvent(new KeyboardEvent("keyup", { key: `NDS_${button}` })); - } -});