diff --git a/docs/switch-development.md b/docs/switch-development.md index 6cec232d..5198814a 100644 --- a/docs/switch-development.md +++ b/docs/switch-development.md @@ -211,3 +211,26 @@ scripts/build_switch.sh --loose shasum -a 256 .bazinga/work/game.love ``` +## Controller input mapping (NX) + +Measured on Switch OLED (`feat/switch-nx`, love-nx `11.5-nx1`, 1280×720). Both `joystickpressed` and `gamepadpressed` fire for Joy-Con; prefer the gamepad path when `joystick:isGamepad()` is true. + +| Path | Control | Mapping | +| ---- | ------- | ------- | +| `gamepadpressed` | D-pad / left stick | move (via `GamepadMap.DEFAULT_GAMEPAD_BINDINGS`) | +| `gamepadpressed` | `a` / `b` | GB A / B | +| `gamepadpressed` | `start` / `back` | Start / Select | +| `joystickpressed` (raw) | `#1` Nintendo B | GB A | +| `joystickpressed` (raw) | `#2` Nintendo A | GB B | +| `joystickpressed` (raw) | `#3` Y | GB B (measured) | +| `joystickpressed` (raw) | `#4` X | GB A (measured) | +| `joystickpressed` (raw) | `#9` / `#10` | Select / Start (− / +) | + +Implementation: `src/core/GamepadMap.lua` (`NX_RAW_*` tables). Launcher (`RomImporter`) and gameplay (`Input`) share the same converter. + +**Opt-in diagnostics:** create an empty `switch-debug.txt` in the save directory; events flush to `switch.log` at ≤1 Hz with build identity (no ROM/save bytes). + +**Hardware re-test:** P0-07/08 Joy-Con launcher + naming screen — pending operator after T15 software (T16). + +**Suspend/resume audio:** after resume, chip music is stopped to avoid duplicate streams; confirm on hardware during P0-09/10 (T19). + diff --git a/src/core/GamepadMap.lua b/src/core/GamepadMap.lua index 68fd5ff4..851f425a 100644 --- a/src/core/GamepadMap.lua +++ b/src/core/GamepadMap.lua @@ -16,16 +16,28 @@ GamepadMap.RAW_BUTTON_BINDINGS = { [7] = "select", [8] = "start", [9] = "select", [10] = "start", } +-- Switch OLED probe 2026-08-01: love.joystickpressed indices (1-based). +-- Gamepad path still preferred when isGamepad(); raw covers the rest. +GamepadMap.NX_RAW_BUTTON_BINDINGS = { + [1] = "a", [2] = "b", + [3] = "b", [4] = "a", + [9] = "select", [10] = "start", +} + -- Raw index -> gamepad button name for RomImporter routing. GamepadMap.RAW_TO_GAMEPAD_BUTTON = { [1] = "a", [2] = "b", [7] = "back", [8] = "start", [9] = "back", [10] = "start", } +GamepadMap.NX_RAW_TO_GAMEPAD_BUTTON = { + [1] = "a", [2] = "b", + [3] = "y", [4] = "x", + [9] = "back", [10] = "start", +} + -- Test hook: force NX raw tables without stubbing love. GamepadMap._forceNXForTests = false -GamepadMap.NX_RAW_BUTTON_BINDINGS = nil -GamepadMap.NX_RAW_TO_GAMEPAD_BUTTON = nil function GamepadMap._setForceNXForTests(v) GamepadMap._forceNXForTests = not not v @@ -43,7 +55,7 @@ function GamepadMap.mapGamepadButton(button) end function GamepadMap.mapRawButton(index) - if nxActive() and GamepadMap.NX_RAW_BUTTON_BINDINGS then + if nxActive() then local nx = GamepadMap.NX_RAW_BUTTON_BINDINGS[index] if nx then return nx end end @@ -51,7 +63,7 @@ function GamepadMap.mapRawButton(index) end function GamepadMap.mapRawToGamepadButton(index) - if nxActive() and GamepadMap.NX_RAW_TO_GAMEPAD_BUTTON then + if nxActive() then local nx = GamepadMap.NX_RAW_TO_GAMEPAD_BUTTON[index] if nx then return nx end end diff --git a/tests/engine/input_nx_raw_map_test.lua b/tests/engine/input_nx_raw_map_test.lua new file mode 100644 index 00000000..4119dacc --- /dev/null +++ b/tests/engine/input_nx_raw_map_test.lua @@ -0,0 +1,22 @@ +-- NX raw fallback indices measured on OLED hardware (SWNX-11). +package.path = "./?.lua;./?/init.lua;" .. package.path +if not _G.love then _G.love = require("tests.love_stub") end + +local T = require("tests.harness") +local eq = T.eq + +local GamepadMap = require("src.core.GamepadMap") + +GamepadMap._setForceNXForTests(true) + +-- Phase 0 probe: Y→#3, X→#4; Nintendo B/A at #1/#2. +eq(GamepadMap.mapRawButton(3), "b", "NX raw Y (#3) maps to GB B") +eq(GamepadMap.mapRawButton(4), "a", "NX raw X (#4) maps to GB A") +eq(GamepadMap.mapRawToGamepadButton(3), "y", "NX raw #3 routes to gamepad y") +eq(GamepadMap.mapRawToGamepadButton(4), "x", "NX raw #4 routes to gamepad x") +eq(GamepadMap.mapRawButton(9), "select", "NX minus (#9) -> select") +eq(GamepadMap.mapRawButton(10), "start", "NX plus (#10) -> start") + +GamepadMap._setForceNXForTests(false) + +T.finish()