From 5febfaed561b41cf1b47e2824f47392b9f817fa0 Mon Sep 17 00:00:00 2001 From: NyakoFox Date: Mon, 25 Mar 2024 14:38:04 -0300 Subject: [PATCH] Automatic menu buttons --- desktop_version/src/Game.cpp | 132 +++++++++++++++++++++++----- desktop_version/src/Game.h | 4 +- desktop_version/src/Input.cpp | 3 +- desktop_version/src/Input.h | 2 + desktop_version/src/Render.cpp | 10 ++- desktop_version/src/RenderFixed.cpp | 2 + desktop_version/src/Script.cpp | 2 + desktop_version/src/Touch.cpp | 79 +++++++++++++++-- desktop_version/src/Touch.h | 8 ++ 9 files changed, 214 insertions(+), 28 deletions(-) diff --git a/desktop_version/src/Game.cpp b/desktop_version/src/Game.cpp index 16d9d009..e78d8509 100644 --- a/desktop_version/src/Game.cpp +++ b/desktop_version/src/Game.cpp @@ -6559,41 +6559,62 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) } currentmenuname = t; + int buttonyoff = 0; + bool buttonscentered = false; menuyoff = 0; int maxspacing = 30; // maximum value for menuspacing, can only become lower. + bool auto_buttons = true; + bool auto_center = true; menucountdown = 0; menuoptions.clear(); + touch::remove_dynamic_buttons(); switch (t) { case Menu::mainmenu: + { if (ingame_titlemode) { /* We shouldn't be here! */ SDL_assert(0 && "Entering main menu from in-game options!"); break; } + int offset = 0; #if !defined(MAKEANDPLAY) option(loc::gettext("play")); + touch::create_menu_button(80, 96, 160, 26, loc::gettext("play"), offset++); #endif option(loc::gettext("levels")); option(loc::gettext("options")); + touch::create_menu_button(80, 128, 160, 26, loc::gettext("levels"), offset++); + touch::create_menu_button(164, 160, 76, 26, loc::gettext("options"), offset++); if (loc::show_translator_menu) { option(loc::gettext("translator")); + offset++; } option(loc::gettext("credits")); option(loc::gettext("quit")); + + touch::create_menu_button(80, 160, 76, 26, loc::gettext("credits"), offset++); + touch::create_menu_button(80, 192, 160, 26, loc::gettext("quit"), offset++); // TODO: Don't show this on phones! Fine for now, but we have to do it before submitting to app stores. + menuyoff = -10; maxspacing = 15; + auto_buttons = false; + + + break; + } case Menu::playerworlds: + buttonyoff = -16; option(loc::gettext("play a level")); option(loc::gettext("level editor"), !editor_disabled); if (!editor_disabled) { - option(loc::gettext("open level folder"), FILESYSTEM_openDirectoryEnabled()); - option(loc::gettext("show level folder path")); + option(loc::gettext("open level folder"), FILESYSTEM_openDirectoryEnabled(), PR_RTL_XFLIP, false); + option(loc::gettext("show level folder path"), true, PR_RTL_XFLIP, false); } option(loc::gettext("return")); menuyoff = -40; @@ -6710,7 +6731,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) menuxoff = 20; menuyoff = 70-(menuoptions.size()*10); menuspacing = 5; - return; // skip automatic centering, will turn out bad with levels list + auto_center = false; } break; case Menu::quickloadlevel: @@ -6749,6 +6770,8 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("return")); menuyoff = -10; maxspacing = 15; + + buttonscentered = true; break; case Menu::graphicoptions: if (!gameScreen.isForcedFullscreen()) @@ -6766,6 +6789,8 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("return")); menuyoff = -10; maxspacing = 15; + + buttonscentered = true; break; case Menu::ed_settings: option(loc::gettext("change description")); @@ -6826,13 +6851,15 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("gameplay")); option(loc::gettext("graphics")); option(loc::gettext("audio")); - option(loc::gettext("game pad")); + option(loc::gettext("game pad"), true, PR_RTL_XFLIP, false); option(loc::gettext("touch input")); option(loc::gettext("accessibility")); option(loc::gettext("language"), !translator_cutscene_test); option(loc::gettext("return")); menuyoff = 0; maxspacing = 15; + + buttonscentered = true; break; case Menu::speedrunneroptions: option(loc::gettext("glitchrunner mode")); @@ -6844,6 +6871,8 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("return")); menuyoff = 0; maxspacing = 15; + + buttonscentered = true; break; case Menu::setglitchrunner: { @@ -6888,6 +6917,8 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("return")); menuyoff = 0; maxspacing = 15; + + buttonscentered = true; break; case Menu::controller: option(loc::gettext("analog stick sensitivity")); @@ -7004,7 +7035,8 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) menuxoff = 20; menuyoff = 55-(menuoptions.size()*10); menuspacing = 5; - return; // skip automatic centering, will turn out bad with scripts list + auto_center = false; + break; case Menu::translator_maintenance: option(loc::gettext("sync language files")); option(loc::gettext("global statistics"), false); @@ -7048,12 +7080,19 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("unlock secret lab"), !unlock[Unlock_SECRETLAB]); option(loc::gettext("return")); menuyoff = -20; + buttonscentered = true; break; case Menu::credits: option(loc::gettext("next page")); option(loc::gettext("last page")); option(loc::gettext("return")); menuyoff = 64; + + touch::create_menu_button(46 - 16, 200, 76, 26, loc::gettext("last"), 1); + touch::create_menu_button(122, 200, 76, 26, loc::gettext("return"), 2); + touch::create_menu_button(198 + 16, 200, 76, 26, loc::gettext("next"), 0); + auto_buttons = false; + break; case Menu::credits2: case Menu::credits25: @@ -7066,12 +7105,22 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("previous page")); option(loc::gettext("return")); menuyoff = 64; + + touch::create_menu_button(46 - 16, 200, 76, 26, loc::gettext("previous"), 1); + touch::create_menu_button(122, 200, 76, 26, loc::gettext("return"), 2); + touch::create_menu_button(198 + 16, 200, 76, 26, loc::gettext("next"), 0); + auto_buttons = false; break; case Menu::credits6: option(loc::gettext("first page")); option(loc::gettext("previous page")); option(loc::gettext("return")); menuyoff = 64; + + touch::create_menu_button(46 - 16, 200, 76, 26, loc::gettext("previous"), 1); + touch::create_menu_button(122, 200, 76, 26, loc::gettext("return"), 2); + touch::create_menu_button(198 + 16, 200, 76, 26, loc::gettext("first"), 0); + auto_buttons = false; break; case Menu::play: { @@ -7186,6 +7235,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) } else { + buttonscentered = true; if (save_exists()) { option(loc::gettext("continue")); @@ -7238,6 +7288,8 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("return")); menuyoff = 8; maxspacing = 20; + + buttonscentered = true; break; case Menu::intermissionmenu: option(loc::gettext("play intermission 1")); @@ -7350,25 +7402,65 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) break; } - // Automatically center the menu. We must check the width of the menu with the initial horizontal spacing. - // If it's too wide, reduce the horizontal spacing by 5 and retry. - // Try to limit the menu width to 272 pixels: 320 minus 16*2 for square brackets, minus 8*2 padding. - // The square brackets fall outside the menu width (i.e. selected menu options are printed 16 pixels to the left) - bool done_once = false; - int menuwidth = 0; - for (; !done_once || (menuwidth > 272 && menuspacing > 0); maxspacing -= 5) + if (auto_center) { - done_once = true; - menuspacing = maxspacing; - menuwidth = 0; - for (size_t i = 0; i < menuoptions.size(); i++) + // Automatically center the menu. We must check the width of the menu with the initial horizontal spacing. + // If it's too wide, reduce the horizontal spacing by 5 and retry. + // Try to limit the menu width to 272 pixels: 320 minus 16*2 for square brackets, minus 8*2 padding. + // The square brackets fall outside the menu width (i.e. selected menu options are printed 16 pixels to the left) + bool done_once = false; + int menuwidth = 0; + for (; !done_once || (menuwidth > 272 && menuspacing > 0); maxspacing -= 5) { - int width = i*menuspacing + font::len(menuoptions[i].print_flags, menuoptions[i].text); - if (width > menuwidth) - menuwidth = width; + done_once = true; + menuspacing = maxspacing; + menuwidth = 0; + for (size_t i = 0; i < menuoptions.size(); i++) + { + int width = i * menuspacing + font::len(menuoptions[i].print_flags, menuoptions[i].text); + if (width > menuwidth) + menuwidth = width; + } + } + menuxoff = (320 - menuwidth) / 2; + } + + if (auto_buttons) + { + int base_y = 128 + menuyoff + buttonyoff; + + if (buttonscentered) + { + int count = 0; + for (int i = 0; i < menuoptions.size(); i++) + { + if (menuoptions[i].auto_button) + { + count++; + } + } + + base_y = (240 - count * 32) / 2; + } + + int offset = 0; + for (int i = 0; i < (int) menuoptions.size(); i++) + { + if (menuoptions[i].auto_button) + { + touch::create_menu_button( + (320 - 160) / 2, + base_y + offset * 32, + 160, + 26, + menuoptions[i].text, + i, + menuoptions[i].active + ); + offset++; + } } } - menuxoff = (320-menuwidth)/2; } bool Game::can_unlock_ndm(void) diff --git a/desktop_version/src/Game.h b/desktop_version/src/Game.h index 9838f85b..5afe1a3a 100644 --- a/desktop_version/src/Game.h +++ b/desktop_version/src/Game.h @@ -31,6 +31,7 @@ struct MenuOption char text[MENU_TEXT_BYTES]; bool active; uint32_t print_flags; + bool auto_button; }; //Menu IDs @@ -387,12 +388,13 @@ public: int menuspacing; std::vector menustack; - void inline option(const char* text, bool active = true, uint32_t print_flags = PR_RTL_XFLIP) + void inline option(const char* text, bool active = true, uint32_t print_flags = PR_RTL_XFLIP, bool auto_button = true) { MenuOption menuoption; SDL_strlcpy(menuoption.text, text, sizeof(menuoption.text)); menuoption.active = active; menuoption.print_flags = print_flags; + menuoption.auto_button = auto_button; menuoptions.push_back(menuoption); } diff --git a/desktop_version/src/Input.cpp b/desktop_version/src/Input.cpp index 53ab60dd..27094bf0 100644 --- a/desktop_version/src/Input.cpp +++ b/desktop_version/src/Input.cpp @@ -386,7 +386,7 @@ static void slidermodeinput(void) *user_changing_volume = SDL_clamp(*user_changing_volume, 0, USER_VOLUME_MAX); } -static void menuactionpress(void) +void menuactionpress(void) { if (game.menutestmode) { @@ -3165,6 +3165,7 @@ void mapinput(void) if (key.isDown(KEYBOARD_ENTER) || key.isDown(game.controllerButton_map)) game.press_map = true; if ((key.isDown(27) || touch::button_tapped(TOUCH_BUTTON_CANCEL)) && !game.mapheld) { + touch::remove_dynamic_buttons(); game.mapheld = true; if (game.menupage < 9 || (game.menupage >= 20 && game.menupage <= 21)) diff --git a/desktop_version/src/Input.h b/desktop_version/src/Input.h index 90a64a62..f42b067a 100644 --- a/desktop_version/src/Input.h +++ b/desktop_version/src/Input.h @@ -1,6 +1,8 @@ #ifndef INPUT_H #define INPUT_H +void menuactionpress(void); + void titleinput(void); void gameinput(void); diff --git a/desktop_version/src/Render.cpp b/desktop_version/src/Render.cpp index d7dba0f4..596f3d23 100644 --- a/desktop_version/src/Render.cpp +++ b/desktop_version/src/Render.cpp @@ -1932,7 +1932,15 @@ void titlerender(void) if(tg>255) tg=255; if (tb < 0) tb = 0; if(tb>255) tb=255; - graphics.drawmenu(tr, tg, tb, game.currentmenuname); + + if (key.using_touch) + { + touch::render_buttons(tr, tg, tb); + } + else + { + graphics.drawmenu(tr, tg, tb, game.currentmenuname); + } } graphics.drawfade(); diff --git a/desktop_version/src/RenderFixed.cpp b/desktop_version/src/RenderFixed.cpp index c68f484d..73eb8a67 100644 --- a/desktop_version/src/RenderFixed.cpp +++ b/desktop_version/src/RenderFixed.cpp @@ -6,6 +6,7 @@ #include "Enums.h" #include "Map.h" #include "Script.h" +#include "Touch.h" #include "UtilityClass.h" static inline void titleupdatetextcol(void) @@ -227,6 +228,7 @@ void maprenderfixed(void) { graphics.menuoffset = threshold; //go back to gamemode! + touch::remove_dynamic_buttons(); game.mapheld = true; game.gamestate = GAMEMODE; graphics.resumegamemode = false; diff --git a/desktop_version/src/Script.cpp b/desktop_version/src/Script.cpp index 73a7e1cc..d3dc1892 100644 --- a/desktop_version/src/Script.cpp +++ b/desktop_version/src/Script.cpp @@ -2571,6 +2571,8 @@ void scriptclass::startgamemode(const enum StartMode mode) } } + touch::remove_dynamic_buttons(); + /* Containers which need to be reset before gameplay starts * ex. before custom levels get loaded */ diff --git a/desktop_version/src/Touch.cpp b/desktop_version/src/Touch.cpp index a961498c..a4641c5f 100644 --- a/desktop_version/src/Touch.cpp +++ b/desktop_version/src/Touch.cpp @@ -73,6 +73,8 @@ namespace touch void init(void) { scale = 10; + use_buttons = false; + textbox_style = false; for (int i = 0; i < NUM_TOUCH_BUTTONS; i++) { @@ -92,12 +94,82 @@ namespace touch refresh_all_buttons(); } + TouchButton create_button(int x, int y, int width, int height, std::string text) + { + int scale = get_scale(); + + if (width == -1) + { + width = font::len(PR_CEN | (SDL_min((scale - 1), 7) << 0), text.c_str()) + 24 * scale; + } + + if (height == -1) + { + height = font::height(PR_CEN | (SDL_min((scale - 1), 7) << 0)) + 18 * scale; + } + + TouchButton button; + button.x = x; + button.y = y; + button.width = width; + button.height = height; + button.image = NULL; + button.text = text; + button.active = true; + button.core = false; + button.ui = false; + button.down = false; + button.pressed = false; + button.fingerId = -1; + button.type = TOUCH_BUTTON_TYPE_NONE; + button.id = -1; + button.disabled = false; + + return button; + } + + /* Helper function to create menu buttons (very common) in a single line */ + void create_menu_button(int x, int y, int width, int height, std::string text, int id) + { + TouchButton button = create_button(x, y, width, height, text); + button.type = TOUCH_BUTTON_TYPE_MENU; + button.id = id; + + register_button(button); + } + + void create_menu_button(int x, int y, int width, int height, std::string text, int id, bool active) + { + TouchButton button = create_button(x, y, width, height, text); + button.type = TOUCH_BUTTON_TYPE_MENU; + button.id = id; + button.disabled = !active; + + register_button(button); + } + + void register_button(TouchButton button) + { + dynamic_buttons.push_back(button); + + refresh_all_buttons(); + } + + void remove_dynamic_buttons(void) + { + dynamic_buttons.clear(); + refresh_all_buttons(); + } + void on_button_up(TouchButton* button) { bool version2_2 = GlitchrunnerMode_less_than_or_equal(Glitchrunner2_2); switch (button->type) { case TOUCH_BUTTON_TYPE_MENU: + game.currentmenuoption = button->id; + menuactionpress(); + break; case TOUCH_BUTTON_TYPE_NONE: default: break; @@ -165,12 +237,9 @@ namespace touch break; case TITLEMODE: - if (game.menustart) + if (!game.menustart) { - buttons[TOUCH_BUTTON_LEFT].active = true; - buttons[TOUCH_BUTTON_RIGHT].active = true; - buttons[TOUCH_BUTTON_CANCEL].active = true; - buttons[TOUCH_BUTTON_CONFIRM].active = true; + use_buttons = false; } break; case TELEPORTERMODE: diff --git a/desktop_version/src/Touch.h b/desktop_version/src/Touch.h index 119350b8..e9113664 100644 --- a/desktop_version/src/Touch.h +++ b/desktop_version/src/Touch.h @@ -90,6 +90,14 @@ namespace touch void reset(void); void update_buttons(void); + TouchButton create_button(int x, int y, int width, int height, std::string text); + void register_button(TouchButton button); + + void create_menu_button(int x, int y, int width, int height, std::string text, int id); + void create_menu_button(int x, int y, int width, int height, std::string text, int id, bool disabled); + + void remove_dynamic_buttons(void); + void on_button_up(TouchButton* button); void init(void);