diff --git a/desktop_version/lang/en/strings.xml b/desktop_version/lang/en/strings.xml
index f2fdb5da..feb41259 100644
--- a/desktop_version/lang/en/strings.xml
+++ b/desktop_version/lang/en/strings.xml
@@ -213,6 +213,7 @@
+
diff --git a/desktop_version/src/ActionSets.h b/desktop_version/src/ActionSets.h
index ad445ea3..4311c5a9 100644
--- a/desktop_version/src/ActionSets.h
+++ b/desktop_version/src/ActionSets.h
@@ -53,7 +53,9 @@ typedef enum
{
Action_InGame_ACTION,
Action_InGame_Interact,
- Action_InGame_Map
+ Action_InGame_Map,
+ Action_InGame_Restart,
+ Action_InGame_Esc
}
Action_InGame;
diff --git a/desktop_version/src/ButtonGlyphs.cpp b/desktop_version/src/ButtonGlyphs.cpp
index f4126dbf..9580c6f2 100644
--- a/desktop_version/src/ButtonGlyphs.cpp
+++ b/desktop_version/src/ButtonGlyphs.cpp
@@ -68,6 +68,74 @@ ButtonGlyphKey;
static char glyph[GLYPH_TOTAL][5];
+typedef enum
+{
+ LAYOUT_NINTENDO_SWITCH_PRO,
+ LAYOUT_NINTENDO_SWITCH_JOYCON_L,
+ LAYOUT_NINTENDO_SWITCH_JOYCON_R,
+ LAYOUT_DECK,
+ LAYOUT_PLAYSTATION,
+ LAYOUT_XBOX,
+ LAYOUT_GENERIC,
+
+ LAYOUT_TOTAL
+}
+ButtonGlyphLayout;
+
+/* SDL provides Xbox buttons, we'd like to show the correct
+ * (controller-specific) glyphs or labels for those... */
+static const char* glyph_layout[LAYOUT_TOTAL][SDL_CONTROLLER_BUTTON_RIGHTSHOULDER + 1] = {
+ { // NINTENDO_SWITCH_PRO
+ glyph[GLYPH_NINTENDO_DECK_B], glyph[GLYPH_NINTENDO_DECK_A],
+ glyph[GLYPH_NINTENDO_DECK_Y], glyph[GLYPH_NINTENDO_DECK_X],
+ glyph[GLYPH_NINTENDO_MINUS], "HOME", glyph[GLYPH_NINTENDO_PLUS],
+ glyph[GLYPH_NINTENDO_XBOX_LSTICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
+ glyph[GLYPH_NINTENDO_L], glyph[GLYPH_NINTENDO_R]
+ },
+ { // NINTENDO_SWITCH_JOYCON_L
+ glyph[GLYPH_NINTENDO_GENERIC_ACTIONDOWN], glyph[GLYPH_NINTENDO_GENERIC_ACTIONRIGHT],
+ glyph[GLYPH_NINTENDO_GENERIC_ACTIONLEFT], glyph[GLYPH_NINTENDO_GENERIC_ACTIONUP],
+ "CAPTURE", "GUIDE", glyph[GLYPH_NINTENDO_MINUS],
+ glyph[GLYPH_NINTENDO_GENERIC_STICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
+ glyph[GLYPH_NINTENDO_SL], glyph[GLYPH_NINTENDO_SR]
+ },
+ { // NINTENDO_SWITCH_JOYCON_R
+ glyph[GLYPH_NINTENDO_GENERIC_ACTIONDOWN], glyph[GLYPH_NINTENDO_GENERIC_ACTIONRIGHT],
+ glyph[GLYPH_NINTENDO_GENERIC_ACTIONLEFT], glyph[GLYPH_NINTENDO_GENERIC_ACTIONUP],
+ "HOME", "GUIDE", glyph[GLYPH_NINTENDO_PLUS],
+ glyph[GLYPH_NINTENDO_GENERIC_STICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
+ glyph[GLYPH_NINTENDO_SL], glyph[GLYPH_NINTENDO_SR]
+ },
+ { // DECK
+ glyph[GLYPH_NINTENDO_DECK_A], glyph[GLYPH_NINTENDO_DECK_B],
+ glyph[GLYPH_NINTENDO_DECK_X], glyph[GLYPH_NINTENDO_DECK_Y],
+ glyph[GLYPH_XBOX_DECK_VIEW], "GUIDE", glyph[GLYPH_XBOX_DECK_MENU],
+ glyph[GLYPH_PLAYSTATION_DECK_L3], glyph[GLYPH_PLAYSTATION_DECK_R3],
+ glyph[GLYPH_PLAYSTATION_DECK_L1], glyph[GLYPH_PLAYSTATION_DECK_R1]
+ },
+ { // PLAYSTATION
+ glyph[GLYPH_PLAYSTATION_CROSS], glyph[GLYPH_PLAYSTATION_CIRCLE],
+ glyph[GLYPH_PLAYSTATION_SQUARE], glyph[GLYPH_PLAYSTATION_TRIANGLE],
+ glyph[GLYPH_PLAYSTATION_OPTIONS], "PS", glyph[GLYPH_PLAYSTATION_START],
+ glyph[GLYPH_PLAYSTATION_DECK_L3], glyph[GLYPH_PLAYSTATION_DECK_R3],
+ glyph[GLYPH_PLAYSTATION_DECK_L1], glyph[GLYPH_PLAYSTATION_DECK_R1]
+ },
+ { // XBOX
+ glyph[GLYPH_XBOX_A], glyph[GLYPH_XBOX_B],
+ glyph[GLYPH_XBOX_X], glyph[GLYPH_XBOX_Y],
+ glyph[GLYPH_XBOX_DECK_VIEW], "GUIDE", glyph[GLYPH_XBOX_DECK_MENU],
+ glyph[GLYPH_NINTENDO_XBOX_LSTICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
+ glyph[GLYPH_XBOX_LB], glyph[GLYPH_XBOX_RB]
+ },
+ { // GENERIC
+ glyph[GLYPH_NINTENDO_GENERIC_ACTIONDOWN], glyph[GLYPH_NINTENDO_GENERIC_ACTIONRIGHT],
+ glyph[GLYPH_NINTENDO_GENERIC_ACTIONLEFT], glyph[GLYPH_NINTENDO_GENERIC_ACTIONUP],
+ "SELECT", "GUIDE", "START",
+ glyph[GLYPH_NINTENDO_XBOX_LSTICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
+ glyph[GLYPH_GENERIC_L], glyph[GLYPH_GENERIC_R]
+ }
+};
+
void BUTTONGLYPHS_init(void)
{
/* Set glyph array to strings for all the button glyph codepoints (U+EBxx) */
@@ -101,18 +169,57 @@ const char* BUTTONGLYPHS_get_wasd_text(void)
return loc::gettext("Press left/right to move");
}
-const char* BUTTONGLYPHS_get_button(const ActionSet actionset, const Action action)
+static const char* sdlbutton_to_glyph(const SDL_GameControllerButton button)
+{
+ if (button > SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
+ {
+ SDL_assert(0 && "Unhandled button!");
+ return glyph[GLYPH_UNKNOWN];
+ }
+
+ return glyph_layout[LAYOUT_PLAYSTATION][button];
+}
+
+static const char* glyph_for_vector(
+ const std::vector& buttons,
+ const int index
+) {
+ if (index < 0 || index >= (int) buttons.size())
+ {
+ return NULL;
+ }
+
+ return sdlbutton_to_glyph(buttons[index]);
+}
+
+const char* BUTTONGLYPHS_get_button(const ActionSet actionset, const Action action, int binding)
{
/* Given a specific action (like INTERACT in-game),
* return either a (localized) keyboard key string like "ENTER" or "E",
* or a controller button glyph from the table above like glyph[GLYPH_XBOX_Y],
- * to fill into strings like "Press {button} to activate terminal". */
+ * to fill into strings like "Press {button} to activate terminal".
+ *
+ * Normally, set binding = -1. This will return the best keyboard key OR controller glyph.
+ *
+ * If binding >= 0, select a specific CONTROLLER binding glyph,
+ * or NULL if the index is higher than the max binding index. */
+
+ bool show_controller = binding >= 0 || !BUTTONGLYPHS_keyboard_is_active();
+ if (binding < 0)
+ {
+ binding = 0;
+ }
+
switch (actionset)
{
case ActionSet_Menu:
switch (action.Menu)
{
case Action_Menu_Accept:
+ if (show_controller)
+ {
+ return glyph_for_vector(game.controllerButton_flip, binding);
+ }
return loc::gettext("ACTION");
}
break;
@@ -120,16 +227,43 @@ const char* BUTTONGLYPHS_get_button(const ActionSet actionset, const Action acti
switch (action.InGame)
{
case Action_InGame_ACTION:
+ if (show_controller)
+ {
+ return glyph_for_vector(game.controllerButton_flip, binding);
+ }
return loc::gettext("ACTION");
case Action_InGame_Interact:
+ if (show_controller)
+ {
+ return glyph_for_vector(game.controllerButton_interact, binding);
+ }
if (game.separate_interact)
{
return "E";
}
return loc::gettext("ENTER");
+
case Action_InGame_Map:
+ if (show_controller)
+ {
+ return glyph_for_vector(game.controllerButton_map, binding);
+ }
return loc::gettext("ENTER");
+
+ case Action_InGame_Esc:
+ if (show_controller)
+ {
+ return glyph_for_vector(game.controllerButton_esc, binding);
+ }
+ return loc::gettext("ESC");
+
+ case Action_InGame_Restart:
+ if (show_controller)
+ {
+ return glyph_for_vector(game.controllerButton_restart, binding);
+ }
+ return "R";
}
break;
}
@@ -138,4 +272,40 @@ const char* BUTTONGLYPHS_get_button(const ActionSet actionset, const Action acti
return glyph[GLYPH_UNKNOWN];
}
+char* BUTTONGLYPHS_get_all_gamepad_buttons(
+ char* buffer,
+ size_t buffer_len,
+ const ActionSet actionset,
+ const int action
+) {
+ /* Gives a list of all controller bindings, for in the menu */
+ Action union_action;
+ union_action.intval = action;
+ buffer[0] = '\0';
+ size_t cur = 0;
+ const char* glyph;
+ int binding = 0;
+ while ((glyph = BUTTONGLYPHS_get_button(actionset, union_action, binding)))
+ {
+ if (binding > 0 && buffer_len >= 1)
+ {
+ buffer[cur] = '/';
+ cur++;
+ buffer_len--;
+ }
+
+ size_t glyph_len = SDL_strlcpy(&buffer[cur], glyph, buffer_len);
+ if (glyph_len >= buffer_len)
+ {
+ // Truncation occurred, we're done
+ return buffer;
+ }
+ cur += glyph_len;
+ buffer_len -= glyph_len;
+
+ binding++;
+ }
+ return buffer;
+}
+
} // extern "C"
diff --git a/desktop_version/src/ButtonGlyphs.h b/desktop_version/src/ButtonGlyphs.h
index fa31a04d..b6ff4243 100644
--- a/desktop_version/src/ButtonGlyphs.h
+++ b/desktop_version/src/ButtonGlyphs.h
@@ -1,6 +1,7 @@
#ifndef BUTTONGLYPHS_H
#define BUTTONGLYPHS_H
+#include
#include
#include "ActionSets.h"
@@ -15,7 +16,14 @@ void BUTTONGLYPHS_init(void);
bool BUTTONGLYPHS_keyboard_is_available(void);
bool BUTTONGLYPHS_keyboard_is_active(void);
const char* BUTTONGLYPHS_get_wasd_text(void);
-const char* BUTTONGLYPHS_get_button(ActionSet actionset, Action action);
+const char* BUTTONGLYPHS_get_button(ActionSet actionset, Action action, int binding);
+
+char* BUTTONGLYPHS_get_all_gamepad_buttons(
+ char* buffer,
+ size_t buffer_len,
+ ActionSet actionset,
+ int action
+);
#ifdef __cplusplus
} // extern "C"
diff --git a/desktop_version/src/Graphics.cpp b/desktop_version/src/Graphics.cpp
index c0c9c4bc..1b0153bb 100644
--- a/desktop_version/src/Graphics.cpp
+++ b/desktop_version/src/Graphics.cpp
@@ -770,10 +770,14 @@ static void fill_buttons(char* buffer, const size_t buffer_len, const char* line
line,
"b_act:but,"
"b_int:but,"
- "b_map:but",
+ "b_map:but,"
+ "b_res:but,"
+ "b_esc:but",
vformat_button(ActionSet_InGame, Action_InGame_ACTION),
vformat_button(ActionSet_InGame, Action_InGame_Interact),
- vformat_button(ActionSet_InGame, Action_InGame_Map)
+ vformat_button(ActionSet_InGame, Action_InGame_Map),
+ vformat_button(ActionSet_InGame, Action_InGame_Restart),
+ vformat_button(ActionSet_InGame, Action_InGame_Esc)
);
}
diff --git a/desktop_version/src/Render.cpp b/desktop_version/src/Render.cpp
index 5879c88a..f671a96b 100644
--- a/desktop_version/src/Render.cpp
+++ b/desktop_version/src/Render.cpp
@@ -599,13 +599,42 @@ static void menurender(void)
case 3:
case 4:
case 5:
- font::print(PR_CEN, -1, 75, loc::gettext("Flip is bound to: ") + std::string(help.GCString(game.controllerButton_flip)) , tr, tg, tb);
- font::print(PR_CEN, -1, 85, loc::gettext("Enter is bound to: ") + std::string(help.GCString(game.controllerButton_map)), tr, tg, tb);
- font::print(PR_CEN, -1, 95, loc::gettext("Menu is bound to: ") + std::string(help.GCString(game.controllerButton_esc)) , tr, tg, tb);
- font::print(PR_CEN, -1, 105, loc::gettext("Restart is bound to: ") + std::string(help.GCString(game.controllerButton_restart)) , tr, tg, tb);
- font::print(PR_CEN, -1, 115, loc::gettext("Interact is bound to: ") + std::string(help.GCString(game.controllerButton_interact)) , tr, tg, tb);
+ {
+ char buffer_a[SCREEN_WIDTH_CHARS + 1];
+ char buffer_b[SCREEN_WIDTH_CHARS + 1];
+
+ SDL_snprintf(buffer_a, sizeof(buffer_a), "%s%s",
+ loc::gettext("Flip is bound to: "),
+ BUTTONGLYPHS_get_all_gamepad_buttons(buffer_b, sizeof(buffer_b), ActionSet_InGame, Action_InGame_ACTION)
+ );
+ font::print(PR_CEN, -1, 75, buffer_a, tr, tg, tb);
+
+ SDL_snprintf(buffer_a, sizeof(buffer_a), "%s%s",
+ loc::gettext("Enter is bound to: "),
+ BUTTONGLYPHS_get_all_gamepad_buttons(buffer_b, sizeof(buffer_b), ActionSet_InGame, Action_InGame_Map)
+ );
+ font::print(PR_CEN, -1, 85, buffer_a, tr, tg, tb);
+
+ SDL_snprintf(buffer_a, sizeof(buffer_a), "%s%s",
+ loc::gettext("Menu is bound to: "),
+ BUTTONGLYPHS_get_all_gamepad_buttons(buffer_b, sizeof(buffer_b), ActionSet_InGame, Action_InGame_Esc)
+ );
+ font::print(PR_CEN, -1, 95, buffer_a, tr, tg, tb);
+
+ SDL_snprintf(buffer_a, sizeof(buffer_a), "%s%s",
+ loc::gettext("Restart is bound to: "),
+ BUTTONGLYPHS_get_all_gamepad_buttons(buffer_b, sizeof(buffer_b), ActionSet_InGame, Action_InGame_Restart)
+ );
+ font::print(PR_CEN, -1, 105, buffer_a, tr, tg, tb);
+
+ SDL_snprintf(buffer_a, sizeof(buffer_a), "%s%s",
+ loc::gettext("Interact is bound to: "),
+ BUTTONGLYPHS_get_all_gamepad_buttons(buffer_b, sizeof(buffer_b), ActionSet_InGame, Action_InGame_Interact)
+ );
+ font::print(PR_CEN, -1, 115, buffer_a, tr, tg, tb);
break;
}
+ }
break;
diff --git a/desktop_version/src/UtilityClass.cpp b/desktop_version/src/UtilityClass.cpp
index d4d1e972..a2442230 100644
--- a/desktop_version/src/UtilityClass.cpp
+++ b/desktop_version/src/UtilityClass.cpp
@@ -9,38 +9,6 @@
#include "Maths.h"
#include "VFormat.h"
-static const char* GCChar(const SDL_GameControllerButton button)
-{
- switch (button)
- {
- case SDL_CONTROLLER_BUTTON_A:
- return "A";
- case SDL_CONTROLLER_BUTTON_B:
- return "B";
- case SDL_CONTROLLER_BUTTON_X:
- return "X";
- case SDL_CONTROLLER_BUTTON_Y:
- return "Y";
- case SDL_CONTROLLER_BUTTON_BACK:
- return "BACK";
- case SDL_CONTROLLER_BUTTON_GUIDE:
- return "GUIDE";
- case SDL_CONTROLLER_BUTTON_START:
- return "START";
- case SDL_CONTROLLER_BUTTON_LEFTSTICK:
- return "L3";
- case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
- return "R3";
- case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
- return "LB";
- case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
- return "RB";
- default:
- SDL_assert(0 && "Unhandled button!");
- return NULL;
- }
-}
-
int ss_toi(const std::string& str)
{
int retval = 0;
@@ -157,20 +125,6 @@ int UtilityClass::Int(const char* str, int fallback /*= 0*/)
return (int) SDL_strtol(str, NULL, 0);
}
-std::string UtilityClass::GCString(const std::vector& buttons)
-{
- std::string retval = "";
- for (size_t i = 0; i < buttons.size(); i += 1)
- {
- retval += GCChar(buttons[i]);
- if ((i + 1) < buttons.size())
- {
- retval += ",";
- }
- }
- return retval;
-}
-
int UtilityClass::hms_to_seconds(int h, int m, int s)
{
return h*3600 + m*60 + s;
diff --git a/desktop_version/src/UtilityClass.h b/desktop_version/src/UtilityClass.h
index 4f6594bc..9543861d 100644
--- a/desktop_version/src/UtilityClass.h
+++ b/desktop_version/src/UtilityClass.h
@@ -3,7 +3,6 @@
#include
#include
-#include
int ss_toi(const std::string& str);
@@ -102,8 +101,6 @@ public:
static int Int(const char* str, int fallback = 0);
- static std::string GCString(const std::vector& buttons);
-
int hms_to_seconds(int h, int m, int s);
void format_time(char* buffer, const size_t buffer_size, int seconds, int frames, bool always_minutes);
diff --git a/desktop_version/src/VFormat.c b/desktop_version/src/VFormat.c
index a485954a..9147835d 100644
--- a/desktop_version/src/VFormat.c
+++ b/desktop_version/src/VFormat.c
@@ -52,7 +52,7 @@ static inline void call_with_button(format_callback callback, void* userdata, in
Action action;
vformat_unbutton(&actionset, &action, vararg_value);
- const char* button_text = BUTTONGLYPHS_get_button(actionset, action);
+ const char* button_text = BUTTONGLYPHS_get_button(actionset, action, -1);
if (button_text == NULL)
{
callback(userdata, "[null]", 6);