diff --git a/docs/controls.md b/docs/controls.md index d938be3c..dc3232f5 100644 --- a/docs/controls.md +++ b/docs/controls.md @@ -14,6 +14,7 @@ amplitude before the strength (0.2 to 1.5, default 0.6; lower makes weak pulses - `nintendo` (default): by position, as on a Switch (the table below). - `xbox`: south A, east B, west X, north Y, as an Xbox pad labels them; on a PlayStation pad cross is A, circle B, square X, triangle Y. +- `playstation`: the same buttons as `xbox` under the PlayStation name: cross A, circle B, square X, triangle Y. - `auto`: `nintendo` for a pad whose east button SDL reports labelled A (Switch Pro Controller, Nintendo-layout pads), `xbox` for the others. The log names the layout in effect (`input: gamepad layout ...`) and each gamepad's first press @@ -28,7 +29,7 @@ letters unless *Button prompts* says otherwise (below). - the letter in the action prompt (top right); - the ocarina's note buttons. -A, B, X and Y show the button the *Button layout* presses: with `nintendo` (by position) Switch A is the right face button, so its prompts show B or circle, B shows A or cross, X shows Y or triangle and Y shows X or square; with `xbox` and `automatic` (by label) A shows A or cross, B shows B or circle, X shows X or square and Y shows Y or triangle. The game reads the font and the interface textures once at start, so a change shows after a restart (the row says so; the menu's Restart button restarts). A button remapped on this tab keeps its old prompt. +A, B, X and Y show the button the *Button layout* presses: with `nintendo` (by position) Switch A is the right face button, so its prompts show B or circle, B shows A or cross, X shows Y or triangle and Y shows X or square; with `xbox`, `playstation` and `automatic` (by label) A shows A or cross, B shows B or circle, X shows X or square and Y shows Y or triangle. The game reads the font and the interface textures once at start, so a change shows after a restart (the row says so; the menu's Restart button restarts). A button remapped on this tab keeps its old prompt. The prompts live in four overlay folders next to the exe, `romfs-patches-xbox`, `romfs-patches-xbox-label`, `romfs-patches-playstation` and `romfs-patches-playstation-label` (by position and by label), which `runtime/romfs.c` loads in place of the game's files. `tools/pad_prompts.py` draws them on the player's machine from the player's own RomFS and a folder of button art, which the repository does not hold: diff --git a/nx2navi.ini.example b/nx2navi.ini.example index 37404a5c..3f4658cb 100644 --- a/nx2navi.ini.example +++ b/nx2navi.ini.example @@ -288,7 +288,8 @@ # rumble_triggers = 0 # Which gamepad button is Switch A: nintendo (the default: the Switch's buttons by position on any pad, east A, # south B, west Y, north X), xbox (south A, east B, west X, north Y, as an Xbox pad labels them; cross A on a -# PlayStation pad) or auto (nintendo for a pad with Nintendo labels, xbox for the others). The bind_ lines below apply +# PlayStation pad), playstation (the same as xbox: cross A, circle B, square X, triangle Y) or auto (nintendo for a +# pad with Nintendo labels, xbox for the others). The bind_ lines below apply # on top. Changes while the game runs take effect at once. 0.1.9's xbox_prompts is read as nintendo with prompts = xbox. # pad_layout = nintendo # The buttons the game's prompts show: nintendo (the game's own), xbox (LB RB LT RT, Menu, View) or playstation (L1 R1 diff --git a/runtime/cfg.c b/runtime/cfg.c index cb330feb..32d1a42f 100644 --- a/runtime/cfg.c +++ b/runtime/cfg.c @@ -150,7 +150,7 @@ static const CfgMeta metas[] = { // Sound, Controls, Menu {"VOLUME", "100", "0..100", LIVE}, {"MENU_SOUND_VOLUME", "80", "0..100", LIVE}, {"AUDIO_CHANNELS", "stereo", "auto|stereo|5.1", RESTART}, {"MOTION", "1", "0|1", LIVE}, {"RUMBLE_STRENGTH", "130", "0..300", LIVE}, {"RUMBLE_CURVE", "0.6", "0.2..1.5", LIVE}, - {"RUMBLE_TRIGGERS", "0", "0|1", LIVE}, {"PAD_LAYOUT", "nintendo", "auto|nintendo|xbox", LIVE}, + {"RUMBLE_TRIGGERS", "0", "0|1", LIVE}, {"PAD_LAYOUT", "nintendo", "auto|nintendo|xbox|playstation", LIVE}, {"PROMPTS", "nintendo", "nintendo|xbox|playstation|pc", RESTART}, {"STICK_DEADZONE", "8", "0..50", LIVE}, {"STICK_SENS", "1", "0.1..3", RESTART}, {"LSTICK_DEADZONE", "", "0..50", LIVE}, {"RSTICK_DEADZONE", "", "0..50", LIVE}, diff --git a/runtime/cfg_probe.c b/runtime/cfg_probe.c index ccd6dbd8..674162e7 100644 --- a/runtime/cfg_probe.c +++ b/runtime/cfg_probe.c @@ -319,6 +319,8 @@ int probe_cfg(void) { CHECK(input_pad_layout_pick("xbox_prompts", 0) == PAD_LAYOUT_NINTENDO && input_pad_layout_pick("Xbox_Prompts", 1) == PAD_LAYOUT_NINTENDO, "pad layout: 0.1.9's xbox_prompts keeps the Nintendo positions (only the prompts change, romfs.c overlay_init)"); + CHECK(input_pad_layout_pick("PlayStation", 1) == PAD_LAYOUT_XBOX && input_pad_layout_pick("playstation", 0) == PAD_LAYOUT_XBOX, + "pad layout: playstation plays by label as xbox does (cross A), whatever the pad's labels"); // Button prompts (input_map.c) CHECK(input_prompts_pick(NULL, "auto") == PROMPTS_NINTENDO && input_prompts_pick("", "xbox") == PROMPTS_NINTENDO && input_prompts_pick("Xbox", "nintendo") == PROMPTS_XBOX && input_prompts_pick("nintendo", "xbox") == PROMPTS_NINTENDO, @@ -351,8 +353,8 @@ int probe_cfg(void) { // romfs-patches-xbox shows A as B and romfs-patches-playstation A as circle (tools/pad_prompts.py FACE). That is // right exactly where the layout puts A on the east button, which an Xbox pad labels B and a PlayStation pad circle. input_map_reset(); - static const char *const layouts[] = {"nintendo", "xbox", "auto", "xbox_prompts"}; - for (int i = 0; i < 4; i++) { + static const char *const layouts[] = {"nintendo", "xbox", "playstation", "auto", "xbox_prompts"}; + for (int i = 0; i < 5; i++) { int a_east = input_map_pad_buttons((uint8_t[PAD_COUNT]){[PAD_EAST] = 1}, input_pad_layout_pick(layouts[i], 0)) == 1ull << B_A; CHECK(a_east == EQ(input_prompts_folder(PROMPTS_XBOX, layouts[i]), "romfs-patches-xbox"), "prompts: the folder names A as B exactly when the layout puts A on the button labelled B"); diff --git a/runtime/input_map.c b/runtime/input_map.c index 984b4eff..9c208f75 100644 --- a/runtime/input_map.c +++ b/runtime/input_map.c @@ -291,7 +291,7 @@ void input_map_line(const InputMap *m, int action, char *out, size_t n) { int input_pad_layout_pick(const char *setting, int east_labelled_a) { if (setting && (!strcasecmp(setting, "nintendo") || !strcasecmp(setting, "xbox_prompts"))) return PAD_LAYOUT_NINTENDO; - if (setting && !strcasecmp(setting, "xbox")) return PAD_LAYOUT_XBOX; + if (setting && (!strcasecmp(setting, "xbox") || !strcasecmp(setting, "playstation"))) return PAD_LAYOUT_XBOX; return east_labelled_a ? PAD_LAYOUT_NINTENDO : PAD_LAYOUT_XBOX; } diff --git a/runtime/input_map.h b/runtime/input_map.h index 40c128c2..9718e9fe 100644 --- a/runtime/input_map.h +++ b/runtime/input_map.h @@ -82,9 +82,10 @@ int input_map_bind(const char *button, const char *list); // Applies the NX2NAVI_BIND_ settings (cfg_get) over the defaults: at start, and again when one of them changes. void input_map_configure(void); -// The layout for a NX2NAVI_PAD_LAYOUT value: "nintendo" or "xbox" (any case); "xbox_prompts", 0.1.9's name for the Nintendo -// layout with the Xbox prompts, maps the buttons as "nintendo". Anything else ("auto", NULL) picks Nintendo when the -// pad's east button is labelled A (a Switch Pro Controller, a GuliKit) and Xbox otherwise. +// The layout for a NX2NAVI_PAD_LAYOUT value: "nintendo" or "xbox" (any case); "playstation" is the Xbox layout under its +// PlayStation name (cross A, circle B, square X, triangle Y); "xbox_prompts", 0.1.9's name for the Nintendo layout with +// the Xbox prompts, maps the buttons as "nintendo". Anything else ("auto", NULL) picks Nintendo when the pad's east +// button is labelled A (a Switch Pro Controller, a GuliKit) and Xbox otherwise. int input_pad_layout_pick(const char *setting, int east_labelled_a); // Button prompts (NX2NAVI_PROMPTS): the controller whose buttons the game's prompts show. Nintendo: the game's own. Xbox diff --git a/runtime/ui/menu.cpp b/runtime/ui/menu.cpp index 86971713..d33fee27 100644 --- a/runtime/ui/menu.cpp +++ b/runtime/ui/menu.cpp @@ -942,13 +942,16 @@ static void tab_sound(float scale) { // A confirmation is open (Controls: restart, Advanced: reset): B and Esc close it, not the menu; L / R stay on the page. static bool g_modal; -// The note under the layout: Automatic with a gamepad connected, the layout it picked ("in use: Xbox"). NULL otherwise. +// The note under the layout: Automatic with a gamepad connected, the layout it picked ("in use: Xbox"), named +// PlayStation for a pad whose bottom button is labelled cross. NULL otherwise. static const char *pad_layout_note(void) { int got = input_pad_layout(); if (got < 0 || strcmp(cfg_value("NX2NAVI_PAD_LAYOUT"), "auto")) return nullptr; static char note[96]; - snprintf(note, sizeof note, menu_text(UI_MENU_PAD_LAYOUT_IN_USE), - menu_text(got == PAD_LAYOUT_XBOX ? UI_MENU_PAD_LAYOUT_XBOX : UI_MENU_PAD_LAYOUT_NINTENDO)); + UiTextId name = got != PAD_LAYOUT_XBOX ? UI_MENU_PAD_LAYOUT_NINTENDO + : input_pad_label(PAD_SOUTH) == PAD_LABEL_CROSS ? UI_MENU_PAD_LAYOUT_PLAYSTATION + : UI_MENU_PAD_LAYOUT_XBOX; + snprintf(note, sizeof note, menu_text(UI_MENU_PAD_LAYOUT_IN_USE), menu_text(name)); return note; } @@ -1064,8 +1067,9 @@ static void restart_confirm(float scale) { static void tab_controls(float scale) { opt_begin("##controls", scale); - static const UiTextId layout[] = {UI_MENU_PAD_LAYOUT_AUTO, UI_MENU_PAD_LAYOUT_NINTENDO, UI_MENU_PAD_LAYOUT_XBOX}; - opt_choice("NX2NAVI_PAD_LAYOUT", UI_MENU_PAD_LAYOUT, layout, 3, UI_MENU_PAD_LAYOUT_DESC, pad_layout_note()); + static const UiTextId layout[] = {UI_MENU_PAD_LAYOUT_AUTO, UI_MENU_PAD_LAYOUT_NINTENDO, UI_MENU_PAD_LAYOUT_XBOX, + UI_MENU_PAD_LAYOUT_PLAYSTATION}; + opt_choice("NX2NAVI_PAD_LAYOUT", UI_MENU_PAD_LAYOUT, layout, 4, UI_MENU_PAD_LAYOUT_DESC, pad_layout_note()); // The first three values of the setting (cfg.c): pc stays readable in nx2navi.ini, but nothing makes its files for // this game, so the row leaves it out. The game reads its prompts at start: the row's note says when a restart // would bring others, and the bar's Restart button does it. diff --git a/runtime/ui_text.c b/runtime/ui_text.c index becf25c3..b592e2e7 100644 --- a/runtime/ui_text.c +++ b/runtime/ui_text.c @@ -219,9 +219,12 @@ static const char *const text_en[UI_TEXT_COUNT] = { [UI_MENU_RUMBLE_TRIGGERS] = "Trigger vibration (Xbox)", [UI_MENU_RUMBLE_TRIGGERS_DESC] = "Also vibrates the triggers on Xbox controllers.", [UI_MENU_PAD_LAYOUT] = "Button layout", - [UI_MENU_PAD_LAYOUT_DESC] = "Sets which button acts as A.", + [UI_MENU_PAD_LAYOUT_DESC] = "Sets which button acts as A. Nintendo: by position, A right, B bottom, X top, Y " + "left. Xbox and PlayStation: by label, A bottom, B right, X left, Y top (on a " + "PlayStation pad cross, circle, square and triangle). Automatic: Nintendo for a pad " + "labelled the Nintendo way, by label for the others.", [UI_MENU_PAD_LAYOUT_AUTO] = "Automatic", [UI_MENU_PAD_LAYOUT_NINTENDO] = "Nintendo (A right)", - [UI_MENU_PAD_LAYOUT_XBOX] = "Xbox (A bottom)", + [UI_MENU_PAD_LAYOUT_XBOX] = "Xbox (A bottom)", [UI_MENU_PAD_LAYOUT_PLAYSTATION] = "PlayStation (A bottom)", [UI_MENU_PAD_LAYOUT_IN_USE] = "in use: %s", [UI_MENU_PROMPTS] = "Button prompts", [UI_MENU_PROMPTS_DESC] = "Which controller's buttons the game's prompts show, in text, in the action prompt and on " @@ -691,9 +694,12 @@ static const char *const text_ru[UI_TEXT_COUNT] = { [UI_MENU_RUMBLE_TRIGGERS] = "Вибрация триггеров (Xbox)", [UI_MENU_RUMBLE_TRIGGERS_DESC] = "Вибрация также на курках контроллера Xbox.", [UI_MENU_PAD_LAYOUT] = "Раскладка кнопок", - [UI_MENU_PAD_LAYOUT_DESC] = "Какая кнопка работает как A.", + [UI_MENU_PAD_LAYOUT_DESC] = "Какая кнопка работает как A. Nintendo: по положению, A справа, B снизу, X сверху, " + "Y слева. Xbox и PlayStation: по подписи, A снизу, B справа, X слева, Y сверху (на " + "контроллере PlayStation крест, круг, квадрат и треугольник). Автоматически: " + "Nintendo для контроллера с подписями Nintendo, по подписи для остальных.", [UI_MENU_PAD_LAYOUT_AUTO] = "Автоматически", [UI_MENU_PAD_LAYOUT_NINTENDO] = "Nintendo (A справа)", - [UI_MENU_PAD_LAYOUT_XBOX] = "Xbox (A снизу)", + [UI_MENU_PAD_LAYOUT_XBOX] = "Xbox (A снизу)", [UI_MENU_PAD_LAYOUT_PLAYSTATION] = "PlayStation (A снизу)", [UI_MENU_PAD_LAYOUT_IN_USE] = "сейчас: %s", [UI_MENU_PROMPTS] = "Подсказки кнопок", [UI_MENU_PROMPTS_DESC] = "Кнопки какого контроллера показывают подсказки игры: в тексте, в подсказке действия и на " @@ -1076,9 +1082,13 @@ static const char *const text_pt_br[UI_TEXT_COUNT] = { [UI_MENU_RUMBLE_TRIGGERS] = "Vibração dos gatilhos (Xbox)", [UI_MENU_RUMBLE_TRIGGERS_DESC] = "Também vibra os gatilhos do controle Xbox.", [UI_MENU_PAD_LAYOUT] = "Layout dos botões", - [UI_MENU_PAD_LAYOUT_DESC] = "Define qual botão funciona como A.", + [UI_MENU_PAD_LAYOUT_DESC] = "Define qual botão funciona como A. Nintendo: pela posição, A à direita, B embaixo, " + "X em cima, Y à esquerda. Xbox e PlayStation: pela letra, A embaixo, B à direita, X à " + "esquerda, Y em cima (num controle PlayStation xis, círculo, quadrado e triângulo). " + "Automático: Nintendo para um controle com as letras da Nintendo, pela letra para os " + "outros.", [UI_MENU_PAD_LAYOUT_AUTO] = "Automático", [UI_MENU_PAD_LAYOUT_NINTENDO] = "Nintendo (A à direita)", - [UI_MENU_PAD_LAYOUT_XBOX] = "Xbox (A embaixo)", + [UI_MENU_PAD_LAYOUT_XBOX] = "Xbox (A embaixo)", [UI_MENU_PAD_LAYOUT_PLAYSTATION] = "PlayStation (A embaixo)", [UI_MENU_PAD_LAYOUT_IN_USE] = "em uso: %s", [UI_MENU_GROUP_STICKS] = "Analógicos", [UI_MENU_LSTICK_DEADZONE] = "Zona morta do analógico esquerdo", @@ -1592,10 +1602,13 @@ static const char *const text_zh_hans[UI_TEXT_COUNT] = { [UI_MENU_RUMBLE_TRIGGERS] = "扳机振动(Xbox)", [UI_MENU_RUMBLE_TRIGGERS_DESC] = "同时震动 Xbox 手柄的扳机。", [UI_MENU_PAD_LAYOUT] = "按键布局", - [UI_MENU_PAD_LAYOUT_DESC] = "设置哪个按键作为 A 键。", + [UI_MENU_PAD_LAYOUT_DESC] = "设置哪个按键作为 A 键。Nintendo:按位置,A 在右、B 在下、X 在上、Y 在左。Xbox 和 PlayStation:" + "按标注,A 在下、B 在右、X 在左、Y 在上(PlayStation 手柄上为叉、圆圈、方块和三角)。自动:" + "Nintendo 标注的手柄用 Nintendo,其他手柄按标注。", [UI_MENU_PAD_LAYOUT_AUTO] = "自动", [UI_MENU_PAD_LAYOUT_NINTENDO] = "Nintendo(A 在右)", [UI_MENU_PAD_LAYOUT_XBOX] = "Xbox(A 在下)", + [UI_MENU_PAD_LAYOUT_PLAYSTATION] = "PlayStation(A 在下)", [UI_MENU_PAD_LAYOUT_IN_USE] = "使用中:%s", [UI_MENU_GROUP_STICKS] = "摇杆", [UI_MENU_LSTICK_DEADZONE] = "左摇杆死区", [UI_MENU_RSTICK_DEADZONE] = "右摇杆死区", diff --git a/runtime/ui_text.h b/runtime/ui_text.h index fd3ecd58..6ed33776 100644 --- a/runtime/ui_text.h +++ b/runtime/ui_text.h @@ -99,7 +99,7 @@ typedef enum { UI_MENU_RUMBLE_CURVE, UI_MENU_RUMBLE_CURVE_DESC, UI_MENU_RUMBLE_TRIGGERS, UI_MENU_RUMBLE_TRIGGERS_DESC, UI_MENU_PAD_LAYOUT, UI_MENU_PAD_LAYOUT_DESC, UI_MENU_PAD_LAYOUT_AUTO, UI_MENU_PAD_LAYOUT_NINTENDO, - UI_MENU_PAD_LAYOUT_XBOX, UI_MENU_PAD_LAYOUT_IN_USE, + UI_MENU_PAD_LAYOUT_XBOX, UI_MENU_PAD_LAYOUT_PLAYSTATION, UI_MENU_PAD_LAYOUT_IN_USE, UI_MENU_PROMPTS, UI_MENU_PROMPTS_DESC, UI_MENU_PROMPTS_NINTENDO, UI_MENU_PROMPTS_XBOX, UI_MENU_PROMPTS_PLAYSTATION, UI_MENU_PROMPTS_PC, // the notes under the prompts: their files are missing or damaged, they change after restart, or why they are off