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);