feat(nds): improve key input system

This commit is contained in:
2026-02-10 16:28:54 +01:00
parent a586c149d8
commit 14b9ee6119
16 changed files with 59 additions and 102 deletions

View File

@@ -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_X = Math.floor(LOGICAL_WIDTH / 2 - QUIT_SIZE / 2);
const QUIT_Y = 135; const QUIT_Y = 135;
useKeyDown((key) => { useKeyDown(({ key }) => {
if (store.isIntro || store.isOutro) return; if (store.isIntro || store.isOutro) return;
switch (key) { switch (key) {

View File

@@ -110,7 +110,7 @@ onClick((x, y) => {
} }
}); });
useKeyDown((key) => { useKeyDown(({ key }) => {
if (props.yOffset !== 0) return; if (props.yOffset !== 0) return;
switch (key) { switch (key) {
case "NDS_START": case "NDS_START":

View File

@@ -78,7 +78,7 @@ onClick(() => {
store.animateOutro(); store.animateOutro();
}); });
useKeyDown((key) => { useKeyDown(({ key }) => {
if (store.isIntro || store.isOutro) return; if (store.isIntro || store.isOutro) return;
switch (key) { switch (key) {

View File

@@ -203,38 +203,15 @@ const { onRender } = useLoop();
const physicalButtonsDown = new Set<string>(); const physicalButtonsDown = new Set<string>();
let mousePressedButton: string | null = null; let mousePressedButton: string | null = null;
const keyToButton: Record<string, string> = { useKeyDown(({ ndsButton }) => {
ArrowUp: "UP", if (ndsButton) {
ArrowDown: "DOWN", physicalButtonsDown.add(ndsButton);
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}` }),
);
} }
}); });
useKeyUp((key) => { useKeyUp(({ ndsButton }) => {
const button = keyToButton[key]; if (ndsButton) {
if (button) { physicalButtonsDown.delete(ndsButton);
physicalButtonsDown.delete(button);
window.dispatchEvent(new KeyboardEvent("keyup", { key: `NDS_${button}` }));
} }
}); });

View File

@@ -162,7 +162,7 @@ onRender((ctx) => {
ctx.fillStyle = `rgba(0, 0, 0, ${store.isIntro ? store.intro.fadeOpacity : store.isOutro ? store.outro.fadeOpacity : 0})`; 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); ctx.fillRect(0, 0, LOGICAL_WIDTH, LOGICAL_HEIGHT);
}); });
useKeyDown((key) => { useKeyDown(({ key }) => {
if ( if (
currentAnimation || currentAnimation ||
store.isIntro || store.isIntro ||

View File

@@ -40,7 +40,7 @@ onMounted(() => {
onUnmounted(() => timeline?.kill()); onUnmounted(() => timeline?.kill());
useKeyDown((key) => { useKeyDown(({ key }) => {
if (!store.showConfirmationPopup) return; if (!store.showConfirmationPopup) return;
if (textProgress.value < 1 && key === "NDS_A") { if (textProgress.value < 1 && key === "NDS_A") {
@@ -73,7 +73,7 @@ useKeyDown((key) => {
} }
}); });
useKeyUp((key) => { useKeyUp(({ key }) => {
if (store.showConfirmationPopup && key === "NDS_A") { if (store.showConfirmationPopup && key === "NDS_A") {
waitingForNdsARelease = false; waitingForNdsARelease = false;
} }

View File

@@ -131,7 +131,7 @@ onClick((x, y) => {
} }
}); });
useKeyDown((key) => { useKeyDown(({ key }) => {
if (isAnimating.value) return; if (isAnimating.value) return;
if (key === "NDS_X") { if (key === "NDS_X") {
handleVisitAll(); handleVisitAll();

View File

@@ -538,7 +538,7 @@ const slide = (rowDir: number, colDir: number) => {
} }
}; };
useKeyDown((key) => { useKeyDown(({ key }) => {
if (isAnimating.value) return; if (isAnimating.value) return;
switch (key) { switch (key) {
// TODO: remove this, testing only // TODO: remove this, testing only

View File

@@ -139,7 +139,7 @@ const select = (col: number, row: number) => {
app.setColor(col, row); app.setColor(col, row);
}; };
useKeyDown((key) => { useKeyDown(({ key }) => {
if (isAnimating.value) return; if (isAnimating.value) return;
switch (key) { switch (key) {

View File

@@ -302,7 +302,7 @@ onRender((ctx) => {
); );
}, 110); }, 110);
useKeyDown((key) => { useKeyDown(({ key }) => {
if (state.value !== "alive") return; if (state.value !== "alive") return;
const newDirection = direction.clone(); const newDirection = direction.clone();

View File

@@ -212,7 +212,7 @@ onRender((ctx) => {
); );
}, 10); }, 10);
useKeyDown((key) => { useKeyDown(({ key }) => {
if (isAnimating.value || !props.selected || props.disabled) return; if (isAnimating.value || !props.selected || props.disabled) return;
switch (key) { switch (key) {
case "NDS_UP": case "NDS_UP":

View File

@@ -221,7 +221,7 @@ export const useButtonNavigation = <T extends Record<string, Rect>>({
} }
}); });
useKeyDown((key) => { useKeyDown(({ key }) => {
if (blockInteractions.value) return; if (blockInteractions.value) return;
const currentButton = selectedButton.value as Entry; const currentButton = selectedButton.value as Entry;

View File

@@ -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) => { export const useKeyDown = (callback: KeyDownCallback) => {
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
callback(event.key); const ndsButton = mapKeyToNDS(event.key);
callback({
key: ndsButton ? `NDS_${ndsButton}` : event.key,
ndsButton,
repeated: event.repeat,
});
}; };
onMounted(() => { onMounted(() => {

View File

@@ -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) => { export const useKeyUp = (callback: KeyUpCallback) => {
const handleKeyUp = (event: KeyboardEvent) => { const handleKeyUp = (event: KeyboardEvent) => {
callback(event.key); const ndsButton = mapKeyToNDS(event.key);
callback({
key: ndsButton ? `NDS_${ndsButton}` : event.key,
ndsButton,
});
}; };
onMounted(() => { onMounted(() => {

View File

@@ -12,63 +12,6 @@ const bottomScreen = useTemplateRef<ScreenInstance>("bottomScreen");
const topScreenCanvas = computed(() => topScreen.value?.canvas ?? null); const topScreenCanvas = computed(() => topScreen.value?.canvas ?? null);
const bottomScreenCanvas = computed(() => bottomScreen.value?.canvas ?? null); const bottomScreenCanvas = computed(() => bottomScreen.value?.canvas ?? null);
const a = useAchievementsStore();
const keyToButton: Record<string, string> = {
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}` }));
}
});
</script> </script>
<template> <template>

17
app/utils/input.ts Normal file
View File

@@ -0,0 +1,17 @@
const KEY_TO_NDS_BUTTON: Record<string, string> = {
ArrowUp: "UP",
ArrowDown: "DOWN",
ArrowLeft: "LEFT",
ArrowRight: "RIGHT",
D: "A",
S: "B",
Z: "X",
Q: "Y",
" ": "SELECT",
Enter: "START",
};
export const mapKeyToNDS = (key: string): string | null => {
key = key.length === 1 ? key.toUpperCase() : key;
return KEY_TO_NDS_BUTTON[key] ?? null;
};