diff --git a/desktop_version/CMakeLists.txt b/desktop_version/CMakeLists.txt index 28b4b4b8db..1621e16c22 100644 --- a/desktop_version/CMakeLists.txt +++ b/desktop_version/CMakeLists.txt @@ -105,6 +105,7 @@ set(VVV_CXX_SRC src/TerminalScripts.cpp src/Textbox.cpp src/Tower.cpp + src/Touch.cpp src/UtilityClass.cpp src/WarpClass.cpp src/XMLUtils.cpp diff --git a/desktop_version/VVVVVV-android/app/build.gradle b/desktop_version/VVVVVV-android/app/build.gradle index 7eaecbdd4d..c40652e738 100644 --- a/desktop_version/VVVVVV-android/app/build.gradle +++ b/desktop_version/VVVVVV-android/app/build.gradle @@ -88,6 +88,9 @@ def zipRepoAssetsTask = tasks.register("zipRepoAssets", Zip) { from('../../lang') { spec -> spec.into('lang') } + from('../../touch') { spec -> + spec.into('graphics') + } archiveFileName.set('repo.zip') destinationDirectory.value(layout.buildDirectory.dir("generated/main/assets")) } diff --git a/desktop_version/lang/ar/strings.xml b/desktop_version/lang/ar/strings.xml index 7c192c4313..bf066ba4a3 100644 --- a/desktop_version/lang/ar/strings.xml +++ b/desktop_version/lang/ar/strings.xml @@ -259,6 +259,16 @@ + + + + + + + + + + @@ -446,6 +456,7 @@ + diff --git a/desktop_version/lang/ca/strings.xml b/desktop_version/lang/ca/strings.xml index fa956d2fad..c42344015b 100644 --- a/desktop_version/lang/ca/strings.xml +++ b/desktop_version/lang/ca/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/cy/strings.xml b/desktop_version/lang/cy/strings.xml index 90b239130a..0e57c8cb65 100644 --- a/desktop_version/lang/cy/strings.xml +++ b/desktop_version/lang/cy/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/de/strings.xml b/desktop_version/lang/de/strings.xml index e869c9db5c..341c266dde 100644 --- a/desktop_version/lang/de/strings.xml +++ b/desktop_version/lang/de/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/en/strings.xml b/desktop_version/lang/en/strings.xml index ee3db7b600..6dfa0c851b 100644 --- a/desktop_version/lang/en/strings.xml +++ b/desktop_version/lang/en/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/eo/strings.xml b/desktop_version/lang/eo/strings.xml index 63fcd00303..02e0211899 100644 --- a/desktop_version/lang/eo/strings.xml +++ b/desktop_version/lang/eo/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/es/strings.xml b/desktop_version/lang/es/strings.xml index c2cfae353a..7765f0ad4a 100644 --- a/desktop_version/lang/es/strings.xml +++ b/desktop_version/lang/es/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/es_419/strings.xml b/desktop_version/lang/es_419/strings.xml index ea6a1cf929..cf2fdc1db8 100644 --- a/desktop_version/lang/es_419/strings.xml +++ b/desktop_version/lang/es_419/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/es_AR/strings.xml b/desktop_version/lang/es_AR/strings.xml index e9bf98b15f..5e04f93a0f 100644 --- a/desktop_version/lang/es_AR/strings.xml +++ b/desktop_version/lang/es_AR/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/fr/strings.xml b/desktop_version/lang/fr/strings.xml index b7f69dc427..789fb1f517 100644 --- a/desktop_version/lang/fr/strings.xml +++ b/desktop_version/lang/fr/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/ga/strings.xml b/desktop_version/lang/ga/strings.xml index bb19ca2cfd..8ee9174143 100644 --- a/desktop_version/lang/ga/strings.xml +++ b/desktop_version/lang/ga/strings.xml @@ -255,6 +255,16 @@ Déan cóip chúltaca, ar eagla na heagla." explanation="translation maintenance + + + + + + + + + + @@ -442,6 +452,7 @@ Déan cóip chúltaca, ar eagla na heagla." explanation="translation maintenance + diff --git a/desktop_version/lang/it/strings.xml b/desktop_version/lang/it/strings.xml index 4a3c32f318..5076089ad3 100644 --- a/desktop_version/lang/it/strings.xml +++ b/desktop_version/lang/it/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/ja/strings.xml b/desktop_version/lang/ja/strings.xml index 0586db9ebe..d509567e5a 100644 --- a/desktop_version/lang/ja/strings.xml +++ b/desktop_version/lang/ja/strings.xml @@ -268,6 +268,16 @@ Escキーを押すと表示を終了する。" explanation="" max="38*6" max_loc + + + + + + + + + + @@ -474,6 +484,7 @@ Steam Deckには対応していません。" explanation="" max="38*5" max_local + diff --git a/desktop_version/lang/ko/strings.xml b/desktop_version/lang/ko/strings.xml index 10ff2981ba..a45d8bc2f2 100755 --- a/desktop_version/lang/ko/strings.xml +++ b/desktop_version/lang/ko/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/nl/strings.xml b/desktop_version/lang/nl/strings.xml index 09a551ae3c..099dc681d9 100644 --- a/desktop_version/lang/nl/strings.xml +++ b/desktop_version/lang/nl/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/pl/strings.xml b/desktop_version/lang/pl/strings.xml index c88a28fa81..f9695fed9a 100644 --- a/desktop_version/lang/pl/strings.xml +++ b/desktop_version/lang/pl/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/pt_BR/strings.xml b/desktop_version/lang/pt_BR/strings.xml index dd5d677b1e..50ba7732ab 100644 --- a/desktop_version/lang/pt_BR/strings.xml +++ b/desktop_version/lang/pt_BR/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/pt_PT/strings.xml b/desktop_version/lang/pt_PT/strings.xml index 51ca859bb6..03b62f7c59 100644 --- a/desktop_version/lang/pt_PT/strings.xml +++ b/desktop_version/lang/pt_PT/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/ru/strings.xml b/desktop_version/lang/ru/strings.xml index 3b310c8842..40fc5f38d4 100644 --- a/desktop_version/lang/ru/strings.xml +++ b/desktop_version/lang/ru/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/szl/strings.xml b/desktop_version/lang/szl/strings.xml index 386402523e..be1407d0b8 100644 --- a/desktop_version/lang/szl/strings.xml +++ b/desktop_version/lang/szl/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/tr/strings.xml b/desktop_version/lang/tr/strings.xml index cd98037d58..7dee251768 100644 --- a/desktop_version/lang/tr/strings.xml +++ b/desktop_version/lang/tr/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/uk/strings.xml b/desktop_version/lang/uk/strings.xml index 33c6fa97b5..185c3c91a7 100644 --- a/desktop_version/lang/uk/strings.xml +++ b/desktop_version/lang/uk/strings.xml @@ -254,6 +254,16 @@ + + + + + + + + + + @@ -440,6 +450,7 @@ + diff --git a/desktop_version/lang/zh/strings.xml b/desktop_version/lang/zh/strings.xml index a9b83ef0ee..3991b8df44 100644 --- a/desktop_version/lang/zh/strings.xml +++ b/desktop_version/lang/zh/strings.xml @@ -260,6 +260,16 @@ + + + + + + + + + + @@ -450,6 +460,7 @@ + diff --git a/desktop_version/lang/zh_TW/strings.xml b/desktop_version/lang/zh_TW/strings.xml index 9d58c94ec5..de8520cfb4 100644 --- a/desktop_version/lang/zh_TW/strings.xml +++ b/desktop_version/lang/zh_TW/strings.xml @@ -260,6 +260,16 @@ + + + + + + + + + + @@ -450,6 +460,7 @@ + diff --git a/desktop_version/src/Editor.cpp b/desktop_version/src/Editor.cpp index 2a04509e7a..5cc397a1c0 100644 --- a/desktop_version/src/Editor.cpp +++ b/desktop_version/src/Editor.cpp @@ -2653,7 +2653,14 @@ void editorclass::tool_place() static void creategameoptions(void) { - game.createmenu(Menu::options); + if (key.using_touch) + { + game.createmenu(Menu::gameplayoptions); + } + else + { + game.createmenu(Menu::options); + } } static void nextbgcolor(void) diff --git a/desktop_version/src/FileSystemUtils.cpp b/desktop_version/src/FileSystemUtils.cpp index 5f61146ad4..c76aa529fc 100644 --- a/desktop_version/src/FileSystemUtils.cpp +++ b/desktop_version/src/FileSystemUtils.cpp @@ -314,6 +314,8 @@ int FILESYSTEM_init(char *argvZero, char* baseDir, char *assetsPath, char* langD doesFontsDirExist = mount_pre_datazip(NULL, "fonts", "graphics/", fontsDir); + mount_pre_datazip(NULL, "touch", "graphics/", NULL); + /* Mount the stock content last */ if (assetsPath) { diff --git a/desktop_version/src/Game.cpp b/desktop_version/src/Game.cpp index b33333a029..c6524bfb03 100644 --- a/desktop_version/src/Game.cpp +++ b/desktop_version/src/Game.cpp @@ -27,6 +27,7 @@ #include "RoomnameTranslator.h" #include "Screen.h" #include "Script.h" +#include "Touch.h" #include "Unused.h" #include "UTF8.h" #include "UtilityClass.h" @@ -383,7 +384,19 @@ void Game::init(void) checkpoint_saving = false; #endif + languagepage = 0; + setdefaultcontrollerbuttons(); + + tutorial_mode = false; + tutorial_state = 0; + tutorial_timer = 0; + + tutorial_screen_pos = 0; + tutorial_touch_timer = 0; + tutorial_flip = 0; + + seen_touch_tutorial = false; } void Game::setdefaultcontrollerbuttons(void) @@ -4942,6 +4955,16 @@ void Game::deserializesettings(tinyxml2::XMLElement* dataNode, struct ScreenSett key.sensitivity = help.Int(pText); } + if (SDL_strcmp(pKey, "touchscale") == 0) + { + touch::scale = help.Int(pText); + } + + if (SDL_strcmp(pKey, "touchstyle") == 0) + { + touch::style = (TouchControlStyle) (help.Int(pText) % NUM_TOUCH_STYLES); + } + if (SDL_strcmp(pKey, "lang") == 0) { loc::lang = std::string(pText); @@ -4971,6 +4994,11 @@ void Game::deserializesettings(tinyxml2::XMLElement* dataNode, struct ScreenSett { checkpoint_saving = help.Int(pText); } + + if (SDL_strcmp(pKey, "seen_touch_tutorial") == 0) + { + seen_touch_tutorial = help.Int(pText); + } } setdefaultcontrollerbuttons(); @@ -5224,6 +5252,9 @@ void Game::serializesettings(tinyxml2::XMLElement* dataNode, const struct Screen xml::update_tag(dataNode, "controllerSensitivity", key.sensitivity); + xml::update_tag(dataNode, "touchscale", touch::scale); + xml::update_tag(dataNode, "touchstyle", touch::style); + xml::update_tag(dataNode, "lang", loc::lang.c_str()); xml::update_tag(dataNode, "lang_set", (int) loc::lang_set); xml::update_tag(dataNode, "english_sprites", (int) loc::english_sprites); @@ -5231,6 +5262,7 @@ void Game::serializesettings(tinyxml2::XMLElement* dataNode, const struct Screen xml::update_tag(dataNode, "roomname_translator", (int) roomname_translator::enabled); xml::update_tag(dataNode, "checkpoint_saving", (int) checkpoint_saving); + xml::update_tag(dataNode, "seen_touch_tutorial", (int) seen_touch_tutorial); } static bool settings_loaded = false; @@ -6568,11 +6600,14 @@ void Game::returntomenu(enum Menu::MenuName t) if (is_the_menu_we_want) { - break; + return; } i--; } + + // If we didn't find the menu we wanted, just go to the menu we wanted + createmenu(t); } void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) @@ -6594,41 +6629,70 @@ 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; + int button_height = 26; + int button_spacing = 8; 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 - 8 + 16, 160, 26, loc::gettext("play"), offset++); #endif option(loc::gettext("levels")); option(loc::gettext("options")); + offset++; + // Temporarily commented out + //touch::create_menu_button(80, 130 - 8, 160, 26, loc::gettext("levels"), offset++); + touch::create_menu_button(80, 164 - 8 - 34 + 16, 128, 26, loc::gettext("options"), offset++); if (loc::show_translator_menu) { option(loc::gettext("translator")); + offset++; } option(loc::gettext("credits")); option(loc::gettext("quit")); + + // Create the language button + TouchButton button = touch::create_button(214, 164 - 8 - 34 + 16, 26, 26, ""); + button.type = TOUCH_BUTTON_TYPE_MENU_LANGUAGE; + button.id = -1; + + touch::register_button(button); + + touch::create_menu_button(80, 198 - 8 - 34 + 16, 160, 26, loc::gettext("credits"), offset++); + 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; @@ -6745,7 +6809,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: @@ -6770,11 +6834,14 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) menuyoff = -20; break; case Menu::gameplayoptions: + { + int offset = 0; #if !defined(MAKEANDPLAY) if (ingame_titlemode && unlock[Unlock_FLIPMODE]) #endif { - option(loc::gettext("flip mode")); + option(loc::gettext("flip mode")); + offset++; } option(loc::gettext("toggle fps")); option(loc::gettext("speedrun options")); @@ -6784,16 +6851,32 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("return")); menuyoff = -10; maxspacing = 15; + auto_buttons = false; + + touch::create_toggle_button((320 - 160) / 2, 120 - 32, 160, 12, loc::gettext("limit to 30 fps"), offset, !over30mode); + touch::create_toggle_button((320 - 160) / 2, 120 - 32 + 16, 160, 12, loc::gettext("translucent room name bg"), -3, graphics.translucentroomname); + touch::create_toggle_button((320 - 160) / 2, 120 - 32 + 32, 160, 12, loc::gettext("checkpoint saving"), -4, checkpoint_saving); + + touch::create_menu_button(46 - 16, 200, 76, 26, loc::gettext("previous"), -2); + touch::create_menu_button(122, 200, 76, 26, loc::gettext("return"), offset + 5); + touch::create_menu_button(198 + 16, 200, 76, 26, loc::gettext("next"), -1); break; + } case Menu::graphicoptions: + { + int optionid = 4; + int scalingid = 0; if (!gameScreen.isForcedFullscreen()) { option(loc::gettext("toggle fullscreen")); + optionid++; + scalingid++; } option(loc::gettext("scaling mode")); if (!gameScreen.isForcedFullscreen()) { option(loc::gettext("resize to nearest"), gameScreen.isWindowed); + optionid++; } option(loc::gettext("toggle filter")); option(loc::gettext("toggle analogue")); @@ -6801,7 +6884,18 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("return")); menuyoff = -10; maxspacing = 15; + auto_buttons = false; + + touch::create_menu_button((320 - 160) / 2, 120 - 32, 160, button_height, loc::gettext("scaling mode"), scalingid); + touch::create_toggle_button((320 - 160) / 2, 120 + 16, 160, 12, loc::gettext("filtered screen"), optionid - 3, gameScreen.isFiltered); + touch::create_toggle_button((320 - 160) / 2, 120 + 32, 160, 12, loc::gettext("analogue mode"), optionid - 2, gameScreen.badSignalEffect); + touch::create_toggle_button((320 - 160) / 2, 120 + 48, 160, 12, loc::gettext("vsync"), optionid - 1, gameScreen.vsync); + + touch::create_menu_button(46 - 16, 200, 76, 26, loc::gettext("previous"), -2); + touch::create_menu_button(122, 200, 76, 26, loc::gettext("return"), optionid); + touch::create_menu_button(198 + 16, 200, 76, 26, loc::gettext("next"), -1); break; + } case Menu::ed_settings: option(loc::gettext("change description")); option(loc::gettext("edit scripts")); @@ -6861,12 +6955,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")); @@ -6878,6 +6975,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: { @@ -6901,19 +7000,42 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) maxspacing = 15; break; case Menu::audiooptions: + { + int offset = 0; option(loc::gettext("music volume")); option(loc::gettext("sound volume")); if (music.mmmmmm) { + offset++; option(loc::gettext("soundtrack")); } option(loc::gettext("return")); menuyoff = 0; maxspacing = 15; + + auto_buttons = false; + + touch::create_slider_button((320 - 160) / 2, 120 - 32, 160, 48, loc::gettext("music volume"), &music.user_music_volume, 0, USER_VOLUME_MAX); + touch::create_slider_button((320 - 160) / 2, 120 + 32, 160, 48, loc::gettext("sound volume"), &music.user_sound_volume, 0, USER_VOLUME_MAX); + + if (music.mmmmmm) + { + touch::create_toggle_button((320 - 160) / 2, 200 - 48, 160, 12, loc::gettext("MMMMMM soundtrack"), offset, music.usingmmmmmm); + } + + touch::create_menu_button(46 - 16, 200, 76, 26, loc::gettext("previous"), -2); + touch::create_menu_button(122, 200, 76, 26, loc::gettext("return"), offset + 2); + touch::create_menu_button(198 + 16, 200, 76, 26, loc::gettext("next"), -1); break; + } case Menu::accessibility: + { + int offset = -1; #if !defined(MAKEANDPLAY) option(loc::gettext("unlock play modes")); + // For now, we're not going to allow the player to unlock play modes from the options menu, until we come up with a good UI for it. + //touch::create_menu_button((320 - 160) / 2, 120 - 32, 160, 26, loc::gettext("unlock play modes"), 0); + offset = 0; #endif option(loc::gettext("invincibility"), !ingame_titlemode || !incompetitive()); option(loc::gettext("slowdown"), !ingame_titlemode || !incompetitive()); @@ -6923,7 +7045,20 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("return")); menuyoff = 0; maxspacing = 15; + + auto_buttons = false; + + touch::create_toggle_button((320 - 160) / 2, 120 - 24 - 8, 160, 12, loc::gettext("invincibility"), offset + 1, map.invincibility); + touch::create_slider_button((320 - 160) / 2, 120 - 16, 160, 48, loc::gettext("game speed"), &slowdown, 12, 30); + touch::create_toggle_button((320 - 160) / 2, 120 + 32, 160, 12, loc::gettext("animated backgrounds"), offset + 3, !colourblindmode); + touch::create_toggle_button((320 - 160) / 2, 120 + 48, 160, 12, loc::gettext("screen effects"), offset + 4, !noflashingmode); + touch::create_toggle_button((320 - 160) / 2, 120 + 64, 160, 12, loc::gettext("text outline"), offset + 5, !graphics.notextoutline); + + touch::create_menu_button(46 - 16, 200, 76, 26, loc::gettext("previous"), -2); + touch::create_menu_button(122, 200, 76, 26, loc::gettext("return"), offset + 6); + touch::create_menu_button(198 + 16, 200, 76, 26, loc::gettext("next"), -1); break; + } case Menu::controller: option(loc::gettext("analog stick sensitivity")); option(loc::gettext("bind flip")); @@ -6934,8 +7069,27 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("return")); menuyoff = 0; maxspacing = 10; + break; + case Menu::touch_input: + option(loc::gettext("control style")); + option(loc::gettext("ui scale")); + option(loc::gettext("return")); + menuyoff = 0; + maxspacing = 15; + + auto_buttons = false; + + touch::create_menu_button((320 - 160) / 2, 120 - 32, 160, 26, loc::gettext("control style"), 0); + + touch::create_slider_button((320 - 160) / 2, 120 + 16 + 16, 160, 48, loc::gettext("ui scale"), &touch::scale, 5, 20); + + touch::create_menu_button(46 - 16, 200, 76, 26, loc::gettext("previous"), -2); + touch::create_menu_button(122, 200, 76, 26, loc::gettext("return"), 2); + touch::create_menu_button(198 + 16, 200, 76, 26, loc::gettext("next"), -1); + break; case Menu::language: + auto_buttons = false; if (loc::languagelist.empty()) { option(loc::gettext("ok")); @@ -6943,25 +7097,59 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) } else { + int button_count = -1; for (size_t i = 0; i < loc::languagelist.size(); i++) { + int button_x = 0; + int button_y = 0; + bool spawn_buttons = false; + if (i >= (languagepage * 12)) + { + spawn_buttons = true; + button_count++; + button_x = (button_count >= 6) ? 240 : 80; + button_y = 32 + 26 * (button_count % 6); + if (button_count >= 12) + { + spawn_buttons = false; + } + } + if (loc::languagelist[i].nativename.empty()) { option(loc::languagelist[i].code.c_str()); + + if (spawn_buttons) + { + int button_width = SDL_max(136, font::len(PR_1X, loc::languagelist[i].code.c_str()) + 16); + touch::create_menu_button(button_x - button_width / 2, button_y, button_width, 24, loc::languagelist[i].code.c_str(), i); + } } else { + Uint8 flags = PR_FONT_IDX(loc::languagelist[i].font_idx, loc::languagelist[i].rtl); option( loc::languagelist[i].nativename.c_str(), true, - PR_FONT_IDX(loc::languagelist[i].font_idx, loc::languagelist[i].rtl) + flags ); + + if (spawn_buttons) + { + int button_width = SDL_max(136, font::len(flags, loc::languagelist[i].nativename.c_str()) + 16); + touch::create_menu_button_flags(button_x - button_width / 2, button_y, button_width, 24, loc::languagelist[i].nativename.c_str(), i, flags); + } } } menuyoff = 70-(menuoptions.size()*10); maxspacing = 5; } + + touch::create_menu_button(46 - 16, 200, 76, 26, loc::gettext("previous"), -2); + touch::create_menu_button(122, 200, 76, 26, loc::gettext("ok"), -3); + touch::create_menu_button(198 + 16, 200, 76, 26, loc::gettext("next"), -1); + break; case Menu::translator_main: option(loc::gettext("translator options")); @@ -7032,7 +7220,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); @@ -7059,6 +7248,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("no, return to options")); option(loc::gettext("yes, enable")); menuyoff = 64; + buttonyoff = -24; break; case Menu::setslowdown: option(loc::gettext("normal speed")); @@ -7076,12 +7266,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: @@ -7094,12 +7291,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: { @@ -7214,6 +7421,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) } else { + buttonscentered = true; if (save_exists()) { option(loc::gettext("continue")); @@ -7227,7 +7435,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) { option(loc::gettext("secret lab")); } - option(loc::gettext("play modes")); + option(loc::gettext("play modes"), true, PR_RTL_XFLIP, false); // Disable an auto button for play modes for now, we haven't done the menu if (save_exists()) { option(loc::gettext("new game")); @@ -7257,6 +7465,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("start new game")); option(loc::gettext("return")); menuyoff = 64; + buttonyoff = -32; break; case Menu::playmodes: option(loc::gettext("time trials"), !nocompetitive_unless_translator()); @@ -7266,6 +7475,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")); @@ -7297,6 +7508,12 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("continue from quicksave")); option(loc::gettext("return")); menuyoff = 20; + auto_buttons = false; + + + touch::create_menu_button(17, 65 - 20 - 32, 286, 90, "", 0); + touch::create_menu_button(17, 65 - 20 + 64, 286, 90, "", 1); + touch::create_menu_button(17, 65 - 20 + 160, 286, 26, loc::gettext("return"), 2); break; case Menu::startnodeathmode: option(loc::gettext("disable cutscenes")); @@ -7378,25 +7595,68 @@ 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) + { + // 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) + { + 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) { - done_once = true; - menuspacing = maxspacing; - menuwidth = 0; - for (size_t i = 0; i < menuoptions.size(); i++) + 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 * (button_height + button_spacing)) / 2; + } + + int offset = 0; + for (int i = 0; i < (int) menuoptions.size(); i++) { - int width = i*menuspacing + font::len(menuoptions[i].print_flags, menuoptions[i].text); - if (width > menuwidth) - menuwidth = width; + if (menuoptions[i].auto_button) + { + int button_width = SDL_max(160, font::len(menuoptions[i].print_flags, menuoptions[i].text) + 16); + touch::create_menu_button( + (320 - button_width) / 2, + base_y + offset * (button_height + button_spacing), + button_width, + button_height, + menuoptions[i].text, + i, + menuoptions[i].active + ); + offset++; + } } } - menuxoff = (320-menuwidth)/2; + + touch::on_menu_create(); } bool Game::can_unlock_ndm(void) @@ -7765,6 +8025,8 @@ static void returntoingametemp(void) { extern Game game; game.returntomenu(game.kludge_ingametemp); + // Hacky fix to prevent touch buttons from being stuck on the screen + touch::remove_dynamic_buttons(); } static void returntoedsettings(void) diff --git a/desktop_version/src/Game.h b/desktop_version/src/Game.h index 05817b052c..092f00bb37 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 @@ -64,6 +65,7 @@ namespace Menu audiooptions, accessibility, controller, + touch_input, language, translator_main, translator_options, @@ -389,12 +391,13 @@ class Game 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); } @@ -607,6 +610,18 @@ class Game int old_screenshot_border_timer; int screenshot_border_timer; bool screenshot_saved_success; + + int languagepage; + + bool tutorial_mode; + int tutorial_state; + int tutorial_timer; + + int tutorial_screen_pos; + int tutorial_touch_timer; + int tutorial_flip; + + bool seen_touch_tutorial; }; #ifndef GAME_DEFINITION diff --git a/desktop_version/src/Graphics.cpp b/desktop_version/src/Graphics.cpp index 0c2dba3561..fe20c3ba0c 100644 --- a/desktop_version/src/Graphics.cpp +++ b/desktop_version/src/Graphics.cpp @@ -20,6 +20,7 @@ #include "RoomnameTranslator.h" #include "Screen.h" #include "Script.h" +#include "Touch.h" #include "UtilityClass.h" #include "VFormat.h" #include "Vlogging.h" @@ -1175,6 +1176,31 @@ void Graphics::draw_texture(SDL_Texture* image, const int x, const int y) copy_texture(image, NULL, &dstrect); } +void Graphics::draw_texture(SDL_Texture* image, const int x, const int y, const int scalex, const int scaley) +{ + int w, h; + + if (query_texture(image, NULL, NULL, &w, &h) != 0) + { + return; + } + + int flip = SDL_FLIP_NONE; + + if (scalex < 0) + { + flip |= SDL_FLIP_HORIZONTAL; + } + if (scaley < 0) + { + flip |= SDL_FLIP_VERTICAL; + } + + const SDL_Rect dstrect = { x, y, w * SDL_abs(scalex), h * SDL_abs(scaley) }; + + copy_texture(image, NULL, &dstrect, 0, NULL, (SDL_RendererFlip)flip); +} + void Graphics::draw_texture_part(SDL_Texture* image, const int x, const int y, const int x2, const int y2, const int w, const int h, const int scalex, const int scaley) { const SDL_Rect srcrect = {x2, y2, w, h}; @@ -3465,6 +3491,8 @@ void Graphics::screenshake(void) get_stretch_info(&rect); copy_texture(tempShakeTexture, NULL, &rect, 0, NULL, flipmode ? SDL_FLIP_VERTICAL : SDL_FLIP_NONE); + + touch::render(); } void Graphics::updatescreenshake(void) @@ -3549,6 +3577,8 @@ void Graphics::render(void) ime_set_rect(&stretch_info); copy_texture(gameTexture, NULL, &stretch_info, 0, NULL, flipmode ? SDL_FLIP_VERTICAL : SDL_FLIP_NONE); + + touch::render(); } void Graphics::renderwithscreeneffects(void) diff --git a/desktop_version/src/Graphics.h b/desktop_version/src/Graphics.h index c37622a2e7..fe1274c416 100644 --- a/desktop_version/src/Graphics.h +++ b/desktop_version/src/Graphics.h @@ -161,6 +161,8 @@ class Graphics void draw_texture(SDL_Texture* image, int x, int y); + void draw_texture(SDL_Texture* image, int x, int y, int scalex, int scaley); + void draw_texture_part(SDL_Texture* image, int x, int y, int x2, int y2, int w, int h, int scalex, int scaley); void draw_grid_tile(SDL_Texture* texture, int t, int x, int y, int width, int height, int scalex, int scaley); @@ -259,6 +261,7 @@ class Graphics int screenshake_y; void draw_window_background(void); + void draw_touch(void); void get_stretch_info(SDL_Rect* rect); diff --git a/desktop_version/src/GraphicsResources.cpp b/desktop_version/src/GraphicsResources.cpp index 571a1f006e..1a352c1665 100644 --- a/desktop_version/src/GraphicsResources.cpp +++ b/desktop_version/src/GraphicsResources.cpp @@ -429,6 +429,23 @@ void GraphicsResources::init(void) im_image10 = LoadImage("graphics/ending.png"); im_image11 = LoadImage("graphics/site4.png", TEX_WHITE); + im_button_left = LoadImage("graphics/buttons/button_left.png"); + im_button_right = LoadImage("graphics/buttons/button_right.png"); + im_button_map = LoadImage("graphics/buttons/button_map.png"); + im_button_globe = LoadImage("graphics/buttons/button_globe.png"); + + im_tutorial_lefthand_far = LoadImage("graphics/tutorial/lefthand_far.png"); + im_tutorial_lefthand_near = LoadImage("graphics/tutorial/lefthand_near.png"); + im_tutorial_lefthand_off = LoadImage("graphics/tutorial/lefthand_off.png"); + im_tutorial_righthand_far = LoadImage("graphics/tutorial/righthand_far.png"); + im_tutorial_righthand_near = LoadImage("graphics/tutorial/righthand_near.png"); + im_tutorial_righthand_off = LoadImage("graphics/tutorial/righthand_off.png"); + + im_tutorial_arrowleft = LoadImage("graphics/tutorial/arrowleft.png"); + im_tutorial_arrowright = LoadImage("graphics/tutorial/arrowright.png"); + + im_tutorial_screen = LoadImage("graphics/tutorial/touchscreen.png"); + im_sprites_translated = NULL; im_flipsprites_translated = NULL; @@ -476,6 +493,23 @@ void GraphicsResources::destroy(void) CLEAR(im_sprites_translated); CLEAR(im_flipsprites_translated); + + CLEAR(im_button_left); + CLEAR(im_button_right); + CLEAR(im_button_map); + CLEAR(im_button_globe); + + CLEAR(im_tutorial_lefthand_far); + CLEAR(im_tutorial_lefthand_near); + CLEAR(im_tutorial_lefthand_off); + CLEAR(im_tutorial_righthand_far); + CLEAR(im_tutorial_righthand_near); + CLEAR(im_tutorial_righthand_off); + + CLEAR(im_tutorial_arrowleft); + CLEAR(im_tutorial_arrowright); + + CLEAR(im_tutorial_screen); #undef CLEAR VVV_freefunc(SDL_FreeSurface, im_sprites_surf); diff --git a/desktop_version/src/GraphicsResources.h b/desktop_version/src/GraphicsResources.h index f7973c680e..9d080a51ef 100644 --- a/desktop_version/src/GraphicsResources.h +++ b/desktop_version/src/GraphicsResources.h @@ -48,6 +48,24 @@ class GraphicsResources SDL_Texture* im_sprites_translated; SDL_Texture* im_flipsprites_translated; + + /* Touch */ + SDL_Texture* im_button_left; + SDL_Texture* im_button_right; + SDL_Texture* im_button_map; + SDL_Texture* im_button_globe; + + SDL_Texture* im_tutorial_lefthand_far; + SDL_Texture* im_tutorial_lefthand_near; + SDL_Texture* im_tutorial_lefthand_off; + SDL_Texture* im_tutorial_righthand_far; + SDL_Texture* im_tutorial_righthand_near; + SDL_Texture* im_tutorial_righthand_off; + + SDL_Texture* im_tutorial_arrowleft; + SDL_Texture* im_tutorial_arrowright; + + SDL_Texture* im_tutorial_screen; }; SDL_Surface* LoadImageSurface(const char* filename); diff --git a/desktop_version/src/Input.cpp b/desktop_version/src/Input.cpp index c00f736206..ff362c46e1 100644 --- a/desktop_version/src/Input.cpp +++ b/desktop_version/src/Input.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -23,6 +25,7 @@ #include "RoomnameTranslator.h" #include "Screen.h" #include "Script.h" +#include "Touch.h" #include "UtilityClass.h" #include "Vlogging.h" @@ -385,7 +388,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) { @@ -422,6 +425,17 @@ static void menuactionpress(void) #undef OPTION_ID + // Special case for touch input: language button! + if (option_id == -1) + { + music.playef(Sound_VIRIDIAN); + loc::loadlanguagelist(); + loc::pre_title_lang_menu = false; + game.createmenu(Menu::language); + game.currentmenuoption = loc::languagelist_curlang; + map.nexttowercolour(); + break; + } switch (option_id) { @@ -453,7 +467,14 @@ static void menuactionpress(void) case 2: //Options music.playef(Sound_VIRIDIAN); - game.createmenu(Menu::options); + if (key.using_touch) + { + game.createmenu(Menu::gameplayoptions); + } + else + { + game.createmenu(Menu::options); + } map.nexttowercolour(); break; case 3: @@ -707,6 +728,22 @@ static void menuactionpress(void) gameScreen.toggleVSync(); game.savestatsandsettings_menu(); } + if (game.currentmenuoption == -2) + { + // gameplay menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::gameplayoptions, true); + map.nexttowercolour(); + processed = true; + } + if (game.currentmenuoption == -1) + { + // audio menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::audiooptions, true); + map.nexttowercolour(); + processed = true; + } if (!processed) { //back @@ -878,6 +915,22 @@ static void menuactionpress(void) break; case Menu::accessibility: { + if (game.currentmenuoption == -2) + { + // touch menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::touch_input, true); + map.nexttowercolour(); + } + if (game.currentmenuoption == -1) + { + // gameplay menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::gameplayoptions, true); + map.nexttowercolour(); + break; + } + int accessibilityoffset = 0; #if !defined(MAKEANDPLAY) accessibilityoffset = 1; @@ -1031,6 +1084,37 @@ static void menuactionpress(void) map.nexttowercolour(); } + if (game.currentmenuoption == -2) + { + // accessibility menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::accessibility, true); + map.nexttowercolour(); + break; + } + if (game.currentmenuoption == -1) + { + // graphics menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::graphicoptions, true); + map.nexttowercolour(); + } + + if (game.currentmenuoption == -3) + { + // For touch: toggle translucent roomname bg + music.playef(Sound_VIRIDIAN); + graphics.translucentroomname = !graphics.translucentroomname; + game.savestatsandsettings_menu(); + } + if (game.currentmenuoption == -4) + { + // For touch: toggle checkpoint saving + music.playef(Sound_VIRIDIAN); + game.checkpoint_saving = !game.checkpoint_saving; + game.savestatsandsettings_menu(); + } + break; } case Menu::options: @@ -1061,12 +1145,18 @@ static void menuactionpress(void) map.nexttowercolour(); break; case 4: + // touch input options + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::touch_input); + map.nexttowercolour(); + break; + case 5: //accessibility options music.playef(Sound_VIRIDIAN); game.createmenu(Menu::accessibility); map.nexttowercolour(); break; - case 5: + case 6: //language options if (game.translator_cutscene_test) { @@ -1135,6 +1225,22 @@ static void menuactionpress(void) map.nexttowercolour(); music.playef(Sound_VIRIDIAN); } + + if (game.currentmenuoption == -2) + { + // gameplay menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::graphicoptions, true); + map.nexttowercolour(); + } + if (game.currentmenuoption == -1) + { + // touch input menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::touch_input, true); + map.nexttowercolour(); + } + break; case Menu::language: { @@ -1142,32 +1248,84 @@ static void menuactionpress(void) music.playef(Sound_VIRIDIAN); - if (loc::languagelist.size() != 0 && (unsigned)game.currentmenuoption < loc::languagelist.size()) + if (game.currentmenuoption == -3) { - /* Update code also used in KeyPoll.cpp. */ - loc::languagelist_curlang = game.currentmenuoption; - loc::lang = loc::languagelist[game.currentmenuoption].code; - loc::loadtext(false); - loc::lang_set = loc::lang_set_current; - graphics.grphx.init_translations(); - } + // return + + if (loc::pre_title_lang_menu) + { + /* Make the title screen appear, we haven't seen it yet. + * game.returnmenu() works because Menu::mainmenu + * is created before the language menu. */ + game.menustart = false; + loc::pre_title_lang_menu = false; + } + else + { + map.nexttowercolour(); + game.currentmenuoption = loc::languagelist_curlang; + } - if (loc::pre_title_lang_menu) + game.returnmenu(); + } + else if (game.currentmenuoption == -2) { - /* Make the title screen appear, we haven't seen it yet. - * game.returnmenu() works because Menu::mainmenu - * is created before the language menu. */ - game.menustart = false; - loc::pre_title_lang_menu = false; + // go left a page (or wrap to end) + game.languagepage = POS_MOD(game.languagepage - 1, (int) SDL_ceilf(loc::languagelist.size() / 12.0)); + loc::loadlanguagelist(); + game.createmenu(Menu::language, true); + game.currentmenuoption = loc::languagelist_curlang; + map.nexttowercolour(); } - - if (prev_lang != loc::lang) + else if (game.currentmenuoption == -1) { - recomputetextboxes(); + // go right a page (or wrap to start) + game.languagepage = POS_MOD(game.languagepage + 1, (int) SDL_ceilf(loc::languagelist.size() / 12.0)); + loc::loadlanguagelist(); + game.createmenu(Menu::language, true); + game.currentmenuoption = loc::languagelist_curlang; + map.nexttowercolour(); + } + else + { + if (loc::languagelist.size() != 0 && (unsigned)game.currentmenuoption < loc::languagelist.size()) + { + /* Update code also used in KeyPoll.cpp. */ + loc::languagelist_curlang = game.currentmenuoption; + loc::lang = loc::languagelist[game.currentmenuoption].code; + loc::loadtext(false); + loc::lang_set = loc::lang_set_current; + graphics.grphx.init_translations(); + } + + if (prev_lang != loc::lang) + { + recomputetextboxes(); + } + + if (!key.using_touch) + { + if (loc::pre_title_lang_menu) + { + /* Make the title screen appear, we haven't seen it yet. + * game.returnmenu() works because Menu::mainmenu + * is created before the language menu. */ + game.menustart = false; + loc::pre_title_lang_menu = false; + } + + game.returnmenu(); + map.nexttowercolour(); + } + else + { + // We need to respawn the buttons + loc::loadlanguagelist(); + game.createmenu(Menu::language, true); + game.currentmenuoption = loc::languagelist_curlang; + } } - game.returnmenu(); - map.nexttowercolour(); game.savestatsandsettings_menu(); break; @@ -1981,6 +2139,41 @@ static void menuactionpress(void) break; } break; + case Menu::touch_input: + switch (game.currentmenuoption) + { + case -2: + // audio menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::audiooptions, true); + map.nexttowercolour(); + break; + case -1: + // accessibility menu + music.playef(Sound_VIRIDIAN); + game.createmenu(Menu::accessibility, true); + map.nexttowercolour(); + break; + case 0: + music.playef(Sound_VIRIDIAN); + touch::style = (TouchControlStyle) ((touch::style + 1) % NUM_TOUCH_STYLES); + break; + case 1: + touch::scale += 5; + music.playef(Sound_VIRIDIAN); + if (touch::scale > 20) + { + touch::scale = 5; + } + game.savestatsandsettings_menu(); + break; + case 2: + music.playef(Sound_VIRIDIAN); + game.returnmenu(); + map.nexttowercolour(); + break; + } + break; case Menu::cleardatamenu: switch (game.currentmenuoption) { @@ -2353,26 +2546,37 @@ void titleinput(void) controller_down |= key.controllerWantsLeft(false); } - if (key.isDown(left) || key.isDown(KEYBOARD_UP) || key.isDown(a) || key.isDown(KEYBOARD_w) || controller_up) + if (key.isDown(left) || key.isDown(KEYBOARD_UP) || key.isDown(a) || key.isDown(KEYBOARD_w) || controller_up || touch::button_tapped(TOUCH_BUTTON_LEFT)) { game.press_left = true; } - if (key.isDown(right) || key.isDown(KEYBOARD_DOWN) || key.isDown(d) || key.isDown(KEYBOARD_s) || controller_down) + if (key.isDown(right) || key.isDown(KEYBOARD_DOWN) || key.isDown(d) || key.isDown(KEYBOARD_s) || controller_down || touch::button_tapped(TOUCH_BUTTON_RIGHT)) { game.press_right = true; } } - if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip)) game.press_action = true; + if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip) + || (!game.menustart ? touch::screen_down() : touch::button_tapped(TOUCH_BUTTON_CONFIRM))) + { + game.press_action = true; + } + //|| key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN)) game.press_action = true; //on menus, up and down don't work as action if (key.isDown(KEYBOARD_ENTER)) game.press_map = true; //In the menu system, all keypresses are single taps rather than holds. Therefore this test has to be done for all presses - if (!game.press_action && !game.press_left && !game.press_right && !key.isDown(27) && !key.isDown(game.controllerButton_esc)) game.jumpheld = false; + if (!game.press_action && !game.press_left && !game.press_right && !key.isDown(27) && !key.isDown(game.controllerButton_esc) + && !touch::button_tapped(TOUCH_BUTTON_CANCEL) && !key.pressed_android_back) + { + game.jumpheld = false; + } + if (!game.press_map) game.mapheld = false; if (!game.jumpheld && graphics.fademode == FADE_NONE) { - if (game.press_action || game.press_left || game.press_right || game.press_map || key.isDown(27) || key.isDown(game.controllerButton_esc)) + if (game.press_action || game.press_left || game.press_right || game.press_map || key.isDown(27) || key.isDown(game.controllerButton_esc) + || touch::button_tapped(TOUCH_BUTTON_CANCEL) || key.pressed_android_back) { game.jumpheld = true; } @@ -2391,7 +2595,7 @@ void titleinput(void) if (game.menustart && game.menucountdown <= 0 - && (key.isDown(27) || key.isDown(game.controllerButton_esc))) + && (key.isDown(27) || key.isDown(game.controllerButton_esc) || touch::button_tapped(TOUCH_BUTTON_CANCEL) || key.pressed_android_back)) { if (game.currentmenuname == Menu::language && loc::pre_title_lang_menu) { @@ -2411,8 +2615,16 @@ void titleinput(void) } else if (game.currentmenuname == Menu::mainmenu) { - game.createmenu(Menu::youwannaquit); - map.nexttowercolour(); + if (key.pressed_android_back) + { + // Minimize the game!!! (Android only) + SDL_MinimizeWindow(gameScreen.m_window); + } + else + { + game.createmenu(Menu::youwannaquit); + map.nexttowercolour(); + } } else { @@ -2522,6 +2734,7 @@ void titleinput(void) { slidermodeinput(); } + touch::update_sliders(); } if (game.currentmenuoption < 0) game.currentmenuoption = game.menuoptions.size()-1; @@ -2568,16 +2781,20 @@ void gameinput(void) game.press_action = false; game.press_interact = false; - if (key.isDown(KEYBOARD_LEFT) || key.isDown(KEYBOARD_a) || key.controllerWantsLeft(false)) + + touch::update_swipe_finger(); + + if (key.isDown(KEYBOARD_LEFT) || key.isDown(KEYBOARD_a) || key.controllerWantsLeft(false) || touch::buttons[TOUCH_BUTTON_LEFT].down || (touch::swipe_delta < -TOUCH_SWIPE_SENSITIVITY)) { game.press_left = true; } - if (key.isDown(KEYBOARD_RIGHT) || key.isDown(KEYBOARD_d) || key.controllerWantsRight(false)) + if (key.isDown(KEYBOARD_RIGHT) || key.isDown(KEYBOARD_d) || key.controllerWantsRight(false) || touch::buttons[TOUCH_BUTTON_RIGHT].down || (touch::swipe_delta > TOUCH_SWIPE_SENSITIVITY)) { game.press_right = true; } if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) - || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || key.isDown(KEYBOARD_w) || key.isDown(KEYBOARD_s)|| key.isDown(game.controllerButton_flip)) + || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || key.isDown(KEYBOARD_w) + || key.isDown(KEYBOARD_s) || key.isDown(game.controllerButton_flip) || touch::touching_right()) { game.press_action = true; } @@ -2589,7 +2806,7 @@ void gameinput(void) } game.press_map = false; - if (key.isDown(KEYBOARD_ENTER) || key.isDown(SDLK_KP_ENTER) || key.isDown(game.controllerButton_map) ) + if (key.isDown(KEYBOARD_ENTER) || key.isDown(SDLK_KP_ENTER) || key.isDown(game.controllerButton_map) || touch::button_tapped(TOUCH_BUTTON_MAP)) { game.press_map = true; } @@ -2606,7 +2823,12 @@ void gameinput(void) { game.press_action = false; if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) - || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || key.isDown(KEYBOARD_w) || key.isDown(KEYBOARD_s) || key.isDown(game.controllerButton_flip)) game.press_action = true; + || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || key.isDown(KEYBOARD_w) + || key.isDown(KEYBOARD_s) || key.isDown(game.controllerButton_flip) || touch::screen_down() + ) + { + game.press_action = true; + } } if (game.press_action && !game.jumpheld) @@ -2639,7 +2861,8 @@ void gameinput(void) //immediately open again //We really need a better input system soon... && !key.isDown(27) - && !key.isDown(game.controllerButton_esc)) + && !key.isDown(game.controllerButton_esc) + && !touch::button_tapped(TOUCH_BUTTON_CANCEL)) { game.mapheld = false; } @@ -2684,6 +2907,13 @@ void gameinput(void) bool enter_pressed = game.press_map && !game.mapheld; bool enter_already_processed = false; bool interact_pressed; + + bool should_move = true; + if (game.tutorial_mode) + { + should_move = game.tutorial_touch_timer >= 8; + } + if (game.separate_interact) { interact_pressed = game.press_interact && !game.interactheld; @@ -2788,15 +3018,25 @@ void gameinput(void) } } - if(game.press_left) + if (game.press_left) { - obj.entities[ie].ax = -3; - obj.entities[ie].dir = 0; + if (should_move) + { + obj.entities[ie].ax = -3; + obj.entities[ie].dir = 0; + } } else if (game.press_right) { - obj.entities[ie].ax = 3; - obj.entities[ie].dir = 1; + if (should_move) + { + obj.entities[ie].ax = 3; + obj.entities[ie].dir = 1; + } + } + else + { + game.tutorial_touch_timer = 0; } } } @@ -2807,6 +3047,10 @@ void gameinput(void) if (game.press_left) { game.tapleft++; + if (game.tutorial_mode) + { + game.tutorial_touch_timer++; + } } else { @@ -2828,6 +3072,10 @@ void gameinput(void) if (game.press_right) { game.tapright++; + if (game.tutorial_mode) + { + game.tutorial_touch_timer++; + } } else { @@ -2984,7 +3232,7 @@ void gameinput(void) } if (!game.mapheld - && (key.isDown(27) || key.isDown(game.controllerButton_esc)) + && (key.isDown(27) || key.isDown(game.controllerButton_esc) || touch::button_tapped(TOUCH_BUTTON_CANCEL)) && (!map.custommode || map.custommodeforreal)) { game.mapheld = true; @@ -2999,9 +3247,16 @@ void gameinput(void) { game.deathseq = 30; } -} -static void mapmenuactionpress(bool version2_2); + if (game.tutorial_mode && game.tutorial_state >= 13) + { + if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_v) || key.isDown(KEYBOARD_ENTER) || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || touch::screen_down()) + { + music.playef(11); + game.tutorial_mode = false; + } + } +} void mapinput(void) { @@ -3106,15 +3361,15 @@ void mapinput(void) controller_down |= key.controllerWantsLeft(false); } - if (key.isDown(left) || key.isDown(KEYBOARD_UP) || key.isDown(a) || key.isDown(KEYBOARD_w)|| controller_up) + if (key.isDown(left) || key.isDown(KEYBOARD_UP) || key.isDown(a) || key.isDown(KEYBOARD_w)|| controller_up || touch::button_tapped(TOUCH_BUTTON_LEFT)) { game.press_left = true; } - if (key.isDown(right) || key.isDown(KEYBOARD_DOWN) || key.isDown(d) || key.isDown(KEYBOARD_s)|| controller_down) + if (key.isDown(right) || key.isDown(KEYBOARD_DOWN) || key.isDown(d) || key.isDown(KEYBOARD_s)|| controller_down || touch::button_tapped(TOUCH_BUTTON_RIGHT)) { game.press_right = true; } - if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip)) + if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip) || touch::button_tapped(TOUCH_BUTTON_CONFIRM)) { game.press_action = true; } @@ -3122,9 +3377,10 @@ void mapinput(void) || (game.menupage >= 20 && game.menupage <= 21) || (game.menupage >= 30 && game.menupage <= 32)) { - if (key.isDown(KEYBOARD_ENTER) || key.isDown(game.controllerButton_map) ) game.press_map = true; + if (key.isDown(KEYBOARD_ENTER) || key.isDown(game.controllerButton_map) || touch::button_tapped(TOUCH_BUTTON_MAP_BACK)) game.press_map = true; if (key.isDown(27) && !game.mapheld) { + touch::remove_dynamic_buttons(); game.mapheld = true; if (game.menupage < 9 || (game.menupage >= 20 && game.menupage <= 21)) @@ -3144,7 +3400,11 @@ void mapinput(void) } else { - if (key.isDown(KEYBOARD_ENTER) || key.isDown(27)|| key.isDown(game.controllerButton_map) ) game.press_map = true; + if (key.isDown(KEYBOARD_ENTER) || key.isDown(27) || key.isDown(game.controllerButton_map) + || touch::button_tapped(TOUCH_BUTTON_CANCEL)) + { + game.press_map = true; + } } //In the menu system, all keypresses are single taps rather than holds. Therefore this test has to be done for all presses @@ -3211,7 +3471,7 @@ void mapinput(void) } } -static void mapmenuactionpress(const bool version2_2) +void mapmenuactionpress(const bool version2_2) { switch (game.menupage) { @@ -3318,7 +3578,14 @@ static void mapmenuactionpress(const bool version2_2) // Set this before we create the menu game.kludge_ingametemp = game.currentmenuname; - game.createmenu(Menu::options); + if (key.using_touch) + { + game.createmenu(Menu::gameplayoptions); + } + else + { + game.createmenu(Menu::options); + } map.nexttowercolour(); break; case 32: @@ -3345,11 +3612,16 @@ void teleporterinput(void) if(graphics.menuoffset==0) { - if (key.isDown(KEYBOARD_LEFT)|| key.isDown(KEYBOARD_a) || key.controllerWantsLeft(false) ) game.press_left = true; - if (key.isDown(KEYBOARD_RIGHT) || key.isDown(KEYBOARD_d)|| key.controllerWantsRight(false) ) game.press_right = true; + if (key.isDown(KEYBOARD_LEFT)|| key.isDown(KEYBOARD_a) || key.controllerWantsLeft(false) || touch::button_tapped(TOUCH_BUTTON_LEFT)) game.press_left = true; + if (key.isDown(KEYBOARD_RIGHT) || key.isDown(KEYBOARD_d)|| key.controllerWantsRight(false) || touch::button_tapped(TOUCH_BUTTON_RIGHT)) game.press_right = true; if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) - || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN)|| key.isDown(KEYBOARD_w)|| key.isDown(KEYBOARD_s) || key.isDown(game.controllerButton_flip)) game.press_action = true; - if (!game.separate_interact && (key.isDown(KEYBOARD_ENTER) || key.isDown(game.controllerButton_map))) + || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || key.isDown(KEYBOARD_w) + || key.isDown(KEYBOARD_s) || key.isDown(game.controllerButton_flip) + || touch::button_tapped(TOUCH_BUTTON_CONFIRM)) + { + game.press_action = true; + } + if (!game.separate_interact && (key.isDown(KEYBOARD_ENTER) || key.isDown(game.controllerButton_map) || touch::button_tapped(TOUCH_BUTTON_CONFIRM))) { game.press_map = true; } @@ -3362,7 +3634,13 @@ void teleporterinput(void) if (!game.press_action && !game.press_left && !game.press_right && !game.press_interact) game.jumpheld = false; if (!game.press_map) game.mapheld = false; - if (key.isDown(27)) + if (touch::button_tapped(TOUCH_BUTTON_MAP_BACK)) + { + // Close teleporter menu + graphics.resumegamemode = true; + music.playef(Sound_VIRIDIAN); + } + else if (key.isDown(27)) { if (!map.custommode || map.custommodeforreal) { @@ -3489,7 +3767,7 @@ void gamecompleteinput(void) graphics.titlebg.bypos += graphics.titlebg.bscroll; game.oldcreditposition = game.creditposition; - if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip)) + if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip) || touch::screen_down()) { game.creditposition -= 6; if (game.creditposition <= -Credits::creditmaxposition) @@ -3537,7 +3815,7 @@ void gamecompleteinput2(void) //Do this here because input comes first game.oldcreditposx = game.creditposx; - if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip)) + if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip) || touch::screen_down()) { game.creditposx++; game.oldcreditposx++; diff --git a/desktop_version/src/Input.h b/desktop_version/src/Input.h index 90a64a6211..4b7422b275 100644 --- a/desktop_version/src/Input.h +++ b/desktop_version/src/Input.h @@ -1,6 +1,9 @@ #ifndef INPUT_H #define INPUT_H +void menuactionpress(void); +void mapmenuactionpress(const bool version2_2); + void titleinput(void); void gameinput(void); diff --git a/desktop_version/src/KeyPoll.cpp b/desktop_version/src/KeyPoll.cpp index 44ef3aecc7..638677df0f 100644 --- a/desktop_version/src/KeyPoll.cpp +++ b/desktop_version/src/KeyPoll.cpp @@ -17,6 +17,7 @@ #include "LocalizationStorage.h" #include "Music.h" #include "Screen.h" +#include "Touch.h" #include "UTF8.h" #include "UtilityClass.h" #include "Vlogging.h" @@ -63,6 +64,8 @@ KeyPoll::KeyPoll(void) linealreadyemptykludge = false; isActive = true; + + using_touch = false; } void KeyPoll::enabletextentry(void) @@ -219,6 +222,25 @@ bool cycle_language(bool should_recompute_textboxes) return should_recompute_textboxes; } +static void remove_finger(int i) +{ + for (int j = 0; j < (int)touch::all_buttons.size(); j++) + { + if (touch::all_buttons[j]->fingerId == touch::fingers[i].id) + { + if (touch::all_buttons[j]->active && touch::all_buttons[j]->pressed && touch::all_buttons[j]->down) + { + touch::on_button_up(touch::all_buttons[j]); + } + touch::all_buttons[j]->down = false; + touch::all_buttons[j]->pressed = false; + touch::all_buttons[j]->fingerId = -1; + } + } + + touch::fingers.erase(touch::fingers.begin() + i); +} + void KeyPoll::Poll(void) { static int raw_mousex = 0; @@ -233,6 +255,14 @@ void KeyPoll::Poll(void) bool should_recompute_textboxes = false; bool active_input_device_changed = false; bool keyboard_was_active = BUTTONGLYPHS_keyboard_is_active(); + int screen_width; + int screen_height; + gameScreen.GetScreenSize(&screen_width, &screen_height); + + touch::reset(); + + pressed_android_back = false; + while (SDL_PollEvent(&evt)) { switch (evt.type) @@ -247,6 +277,11 @@ void KeyPoll::Poll(void) pressedbackspace = true; } + if (evt.key.keysym.sym == SDLK_AC_BACK) + { + pressed_android_back = true; + } + #ifdef __APPLE__ /* OSX prefers the command keys over the alt keys. -flibit */ altpressed = keymap[SDLK_LGUI] || keymap[SDLK_RGUI]; #else @@ -459,6 +494,66 @@ void KeyPoll::Poll(void) break; } + /* Touch Events */ + case SDL_FINGERDOWN: + { + using_touch = true; + + VVV_Finger finger; + finger.pressed = true; + finger.x = evt.tfinger.x * screen_width; + finger.y = evt.tfinger.y * screen_height; + finger.id = evt.tfinger.fingerId; + finger.on_button = false; + touch::fingers.push_back(finger); + + raw_mousex = evt.tfinger.x * screen_width; + raw_mousey = evt.tfinger.y * screen_height; + leftbutton = 1; + break; + } + case SDL_FINGERMOTION: + { + using_touch = true; + + for (int i = 0; i < (int) touch::fingers.size(); i++) + { + if (touch::fingers[i].id == evt.tfinger.fingerId) + { + touch::fingers[i].x = evt.tfinger.x * screen_width; + touch::fingers[i].y = evt.tfinger.y * screen_height; + break; + } + } + + raw_mousex = evt.tfinger.x * screen_width; + raw_mousey = evt.tfinger.y * screen_height; + break; + } + case SDL_FINGERUP: + { + using_touch = true; + + for (int i = (int) touch::fingers.size() - 1; i >= 0; i--) + { + if (touch::fingers[i].id == evt.tfinger.fingerId) + { + // Unpress any buttons that this finger may belong to + remove_finger(i); + } + } + + if (evt.tfinger.fingerId == touch::swipe_finger) + { + touch::swipe_finger = -1; + } + + raw_mousex = evt.tfinger.x * screen_width; + raw_mousey = evt.tfinger.y * screen_height; + leftbutton = 0; + break; + } + /* Window Events */ case SDL_WINDOWEVENT: switch (evt.window.event) @@ -540,6 +635,11 @@ void KeyPoll::Poll(void) switch (evt.type) { case SDL_KEYDOWN: + if (evt.key.keysym.sym != SDLK_AC_BACK) + { + using_touch = false; + } + if (evt.key.repeat == 0) { hidemouse = true; @@ -548,6 +648,7 @@ void KeyPoll::Poll(void) case SDL_TEXTINPUT: case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERAXISMOTION: + using_touch = false; hidemouse = true; break; case SDL_MOUSEMOTION: @@ -580,6 +681,8 @@ void KeyPoll::Poll(void) { recomputetextboxes(); } + + touch::update_buttons(); } bool KeyPoll::isDown(SDL_Keycode key) diff --git a/desktop_version/src/KeyPoll.h b/desktop_version/src/KeyPoll.h index d51a6b2609..37b0e9afed 100644 --- a/desktop_version/src/KeyPoll.h +++ b/desktop_version/src/KeyPoll.h @@ -75,6 +75,9 @@ class KeyPoll bool linealreadyemptykludge; + bool using_touch; + bool pressed_android_back; + private: std::map controllers; std::map buttonmap; diff --git a/desktop_version/src/Logic.cpp b/desktop_version/src/Logic.cpp index 67dd637a97..2bc6f05ad2 100644 --- a/desktop_version/src/Logic.cpp +++ b/desktop_version/src/Logic.cpp @@ -1483,5 +1483,173 @@ void gamelogic(void) #undef gotoroom #undef GOTOROOM + if (game.tutorial_mode) + { + game.tutorial_timer++; + + if (game.tutorial_flip > 0) + { + game.tutorial_flip--; + } + + switch (game.tutorial_state) + { + case 0: + if (game.tutorial_timer > 10) + { + game.tutorial_state = 1; + game.tutorial_timer = 0; + } + break; + case 1: + game.tutorial_screen_pos += 5; + if (game.tutorial_screen_pos >= 100) + { + game.tutorial_screen_pos = 100; + game.tutorial_state = 2; + game.tutorial_timer = 0; + } + break; + case 2: + if (game.tutorial_timer > 30) + { + game.tutorial_timer = 0; + game.tutorial_state = 3; + } + break; + case 3: + game.press_right = true; + if (game.tutorial_timer > 60) + { + game.tutorial_timer = 0; + game.tutorial_state = 4; + } + break; + case 4: + game.press_right = false; + if (game.tutorial_timer > 30) + { + game.tutorial_timer = 0; + game.tutorial_state = 5; + } + break; + case 5: + game.press_left = true; + if (game.tutorial_timer > 60) + { + game.tutorial_timer = 0; + game.tutorial_state = 6; + } + break; + case 6: + game.press_left = false; + if (game.tutorial_timer > 30) + { + game.tutorial_timer = 0; + game.tutorial_state = 7; + } + break; + case 7: + if (game.tutorial_timer > 5) + { + game.tutorial_timer = 0; + game.tutorial_state = 8; + } + break; + case 8: + game.jumppressed = 5; + game.jumpheld = true; + game.press_action = true; + game.tutorial_state = 9; + game.tutorial_flip = 10; + game.tutorial_timer = 0; + break; + case 9: + if (game.tutorial_timer > 45) + { + game.tutorial_timer = 0; + game.tutorial_state = 10; + } + break; + case 10: + game.jumppressed = 5; + game.jumpheld = true; + game.press_action = true; + game.tutorial_state = 11; + game.tutorial_flip = 10; + game.tutorial_timer = 0; + break; + case 11: + if (game.tutorial_timer > 45) + { + game.tutorial_timer = 0; + game.tutorial_state++; + } + break; + case 12: + game.press_left = false; + game.press_right = true; + if (game.tutorial_timer > 28) + { + game.tutorial_timer = 0; + game.tutorial_state++; + } + break; + case 13: + game.press_left = true; + game.press_right = false; + if (game.tutorial_timer == 30) + { + game.jumppressed = 5; + game.jumpheld = true; + game.press_action = true; + game.tutorial_flip = 10; + } + if (game.tutorial_timer > 45) + { + game.tutorial_timer = 0; + game.tutorial_state++; + } + break; + case 14: + game.press_left = false; + game.press_right = true; + if (game.tutorial_timer == 30) + { + if (fRandom() * 100 > 50) + { + game.jumppressed = 5; + game.jumpheld = true; + game.press_action = true; + game.tutorial_flip = 10; + } + } + if (game.tutorial_timer > 45) + { + game.tutorial_timer = 0; + game.tutorial_state++; + } + break; + case 15: + game.press_left = true; + game.press_right = false; + if (game.tutorial_timer == 30) + { + if (fRandom() * 100 > 50) + { + game.jumppressed = 5; + game.jumpheld = true; + game.tutorial_flip = 10; + } + } + if (game.tutorial_timer > 45) + { + game.tutorial_timer = 0; + game.tutorial_state = 14; + } + break; + } + } + level_debugger::logic(); } diff --git a/desktop_version/src/Render.cpp b/desktop_version/src/Render.cpp index 252dc53dc3..5ab3e1338e 100644 --- a/desktop_version/src/Render.cpp +++ b/desktop_version/src/Render.cpp @@ -25,6 +25,7 @@ #include "RoomnameTranslator.h" #include "Screen.h" #include "Script.h" +#include "Touch.h" #include "UtilityClass.h" #include "VFormat.h" @@ -32,6 +33,8 @@ static int tr; static int tg; static int tb; +static bool dont_render_buttons; + struct MapRenderData { int zoom; @@ -197,6 +200,38 @@ static inline void draw_skip_message() ); } +static void draw_summary(Game::Summary* summary, int offset) +{ + font::print( + PR_CEN, -1, 80 - 20 + offset, + loc::gettext_roomname_special(map.currentarea(summary->saverx, summary->savery)), + 25, 255 - (help.glow / 2), 255 - (help.glow / 2) + ); + for (int i = 0; i < 6; i++) + { + graphics.drawcrewman(169 - (3 * 42) + (i * 42), 95 - 20 + offset, i, summary->crewstats[i], true); + } + font::print( + 0, 59, 132 - 20 + offset, + game.giventimestring( + summary->hours, + summary->minutes, + summary->seconds + ), + 255 - (help.glow / 2), 255 - (help.glow / 2), 255 - (help.glow / 2) + ); + char buffer[SCREEN_WIDTH_CHARS + 1]; + vformat_buf(buffer, sizeof(buffer), + loc::gettext("{savebox_n_trinkets|wordy}"), + "savebox_n_trinkets:int", + summary->trinkets + ); + font::print(PR_RIGHT, 262, 132 - 20 + offset, buffer, 255 - (help.glow / 2), 255 - (help.glow / 2), 255 - (help.glow / 2)); + + graphics.draw_sprite(34, 126 - 20 + offset, 50, graphics.col_clock); + graphics.draw_sprite(270, 126 - 20 + offset, 22, graphics.col_trinket); +} + static void menurender(void) { @@ -204,7 +239,13 @@ static void menurender(void) { case Menu::mainmenu: { - const int temp = 50; + int temp = 50; + + if (key.using_touch) + { + temp = 38; + } + graphics.draw_sprite((160 - 96) + 0 * 32, temp, 23, tr, tg, tb); graphics.draw_sprite((160 - 96) + 1 * 32, temp, 23, tr, tg, tb); graphics.draw_sprite((160 - 96) + 2 * 32, temp, 23, tr, tg, tb); @@ -302,64 +343,72 @@ static void menurender(void) break; case Menu::gameplayoptions: { - int gameplayoptionsoffset = 0; + if (key.using_touch) + { + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Gameplay Options"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Adjust various gameplay settings."), tr, tg, tb); + } + else + { + int gameplayoptionsoffset = 0; #if !defined(MAKEANDPLAY) - if (game.ingame_titlemode && game.unlock[Unlock_FLIPMODE]) + if (game.ingame_titlemode && game.unlock[Unlock_FLIPMODE]) #endif - { - gameplayoptionsoffset = 1; - if (game.currentmenuoption == 0) { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Flip Mode"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Flip the entire game vertically."), tr, tg, tb); - if (graphics.setflipmode) + { + gameplayoptionsoffset = 1; + if (game.currentmenuoption == 0) { + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Flip Mode"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Flip the entire game vertically."), tr, tg, tb); + if (graphics.setflipmode) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Currently ENABLED!"), tr, tg, tb); + } + else + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Currently Disabled."), tr / 2, tg / 2, tb / 2); + } + } + } + + if (game.currentmenuoption == gameplayoptionsoffset + 0) + { + //Toggle FPS + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Toggle 30+ FPS"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change whether the game runs at 30 or over 30 FPS."), tr, tg, tb); + + if (!game.over30mode) { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Currently ENABLED!"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: 30 FPS"), tr / 2, tg / 2, tb / 2); } else { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Currently Disabled."), tr/2, tg/2, tb/2); + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: Over 30 FPS"), tr, tg, tb); } + break; } - } - - if (game.currentmenuoption == gameplayoptionsoffset + 0) - { - //Toggle FPS - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Toggle 30+ FPS"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change whether the game runs at 30 or over 30 FPS."), tr, tg, tb); - - if (!game.over30mode) + else if (game.currentmenuoption == gameplayoptionsoffset + 1) { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: 30 FPS"), tr/2, tg/2, tb/2); + //Speedrunner options + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Speedrunner Options"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Access some advanced settings that might be of interest to speedrunners."), tr, tg, tb); } - else + else if (game.currentmenuoption == gameplayoptionsoffset + 2) { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: Over 30 FPS"), tr, tg, tb); + //Advanced options + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Advanced Options"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("All other gameplay settings."), tr, tg, tb); + } + else if (game.currentmenuoption == gameplayoptionsoffset + 3) + { + //Clear Data + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Clear Data"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Delete your main game save data and unlocked play modes."), tr, tg, tb); + } + else if (game.currentmenuoption == gameplayoptionsoffset + 4) + { + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Clear Data"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Delete your custom level save data and completion stars."), tr, tg, tb); } - break; - } - else if (game.currentmenuoption == gameplayoptionsoffset + 1) - { - //Speedrunner options - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Speedrunner Options"), tr, tg, tb); - font::print_wrap(PR_CEN, -1, 65, loc::gettext("Access some advanced settings that might be of interest to speedrunners."), tr, tg, tb); - } - else if (game.currentmenuoption == gameplayoptionsoffset + 2) - { - //Advanced options - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Advanced Options"), tr, tg, tb); - font::print_wrap(PR_CEN, -1, 65, loc::gettext("All other gameplay settings."), tr, tg, tb); - } - else if (game.currentmenuoption == gameplayoptionsoffset + 3) - { - //Clear Data - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Clear Data"), tr, tg, tb); - font::print_wrap(PR_CEN, -1, 65, loc::gettext("Delete your main game save data and unlocked play modes."), tr, tg, tb); - } - else if (game.currentmenuoption == gameplayoptionsoffset + 4) - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Clear Data"), tr, tg, tb); - font::print_wrap(PR_CEN, -1, 65, loc::gettext("Delete your custom level save data and completion stars."), tr, tg, tb); } break; @@ -391,139 +440,166 @@ static void menurender(void) font::print_wrap(PR_CEN, -1, 65, loc::gettext("Rebind your controller's buttons and adjust sensitivity."), tr, tg, tb); break; case 4: + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Touch Input"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change touch input options."), tr, tg, tb); + break; + case 5: font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Accessibility"), tr, tg, tb); font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disable screen effects, enable slowdown modes or invincibility."), tr, tg, tb); break; - case 5: + case 6: font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Language"), tr, tg, tb); font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change the language."), tr, tg, tb); } break; case Menu::graphicoptions: { - int offset = 0; - if (game.currentmenuoption == offset + 0 && !gameScreen.isForcedFullscreen()) + if (key.using_touch) { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Toggle Fullscreen"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change to fullscreen/windowed mode."), tr, tg, tb); - - if (gameScreen.isWindowed) - { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: WINDOWED"), tr, tg, tb); - } - else - { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: FULLSCREEN"), tr, tg, tb); - } + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Graphics Options"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Adjust screen settings."), tr, tg, tb); } - - if (gameScreen.isForcedFullscreen()) + else { - --offset; - } + int offset = 0; + if (game.currentmenuoption == offset + 0 && !gameScreen.isForcedFullscreen()) + { + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Toggle Fullscreen"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change to fullscreen/windowed mode."), tr, tg, tb); - if (game.currentmenuoption == offset + 1) - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Scaling Mode"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Choose letterbox/stretch/integer mode."), tr, tg, tb); + if (gameScreen.isWindowed) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: WINDOWED"), tr, tg, tb); + } + else + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: FULLSCREEN"), tr, tg, tb); + } + } - switch (gameScreen.scalingMode) + if (gameScreen.isForcedFullscreen()) { - case SCALING_INTEGER: - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: INTEGER"), tr, tg, tb); - break; - case SCALING_STRETCH: - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: STRETCH"), tr, tg, tb); - break; - case SCALING_LETTERBOX: - default: - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: LETTERBOX"), tr, tg, tb); - break; + --offset; } - } - if (game.currentmenuoption == offset + 2 && !gameScreen.isForcedFullscreen()) - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Resize to Nearest"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Resize to the nearest window size that is of an integer multiple."), tr, tg, tb); - if (!gameScreen.isWindowed) + + if (game.currentmenuoption == offset + 1) { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("You must be in windowed mode to use this option."), tr, tg, tb); - } - } - if (gameScreen.isForcedFullscreen()) - { - --offset; - } - if (game.currentmenuoption == offset + 3) - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Toggle Filter"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change to nearest/linear filter."), tr, tg, tb); + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Scaling Mode"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Choose letterbox/stretch/integer mode."), tr, tg, tb); - if (gameScreen.isFiltered) + switch (gameScreen.scalingMode) + { + case SCALING_INTEGER: + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: INTEGER"), tr, tg, tb); + break; + case SCALING_STRETCH: + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: STRETCH"), tr, tg, tb); + break; + case SCALING_LETTERBOX: + default: + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: LETTERBOX"), tr, tg, tb); + break; + } + } + if (game.currentmenuoption == offset + 2 && !gameScreen.isForcedFullscreen()) { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: LINEAR"), tr, tg, tb); + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Resize to Nearest"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Resize to the nearest window size that is of an integer multiple."), tr, tg, tb); + if (!gameScreen.isWindowed) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("You must be in windowed mode to use this option."), tr, tg, tb); + } } - else + if (gameScreen.isForcedFullscreen()) { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: NEAREST"), tr, tg, tb); + --offset; } - } + if (game.currentmenuoption == offset + 3) + { + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Toggle Filter"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change to nearest/linear filter."), tr, tg, tb); - if (game.currentmenuoption == offset + 4) - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Analogue Mode"), tr, tg, tb); - font::print_wrap(PR_CEN, -1, 65, loc::gettext("There is nothing wrong with your television set. Do not attempt to adjust the picture."), tr, tg, tb); - } - if (game.currentmenuoption == offset + 5) - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Toggle VSync"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Turn VSync on or off."), tr, tg, tb); + if (gameScreen.isFiltered) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: LINEAR"), tr, tg, tb); + } + else + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: NEAREST"), tr, tg, tb); + } + } - if (!gameScreen.vsync) + if (game.currentmenuoption == offset + 4) { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: VSYNC OFF"), tr/2, tg/2, tb/2); + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Analogue Mode"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("There is nothing wrong with your television set. Do not attempt to adjust the picture."), tr, tg, tb); } - else + if (game.currentmenuoption == offset + 5) { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: VSYNC ON"), tr, tg, tb); + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Toggle VSync"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Turn VSync on or off."), tr, tg, tb); + + if (!gameScreen.vsync) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: VSYNC OFF"), tr / 2, tg / 2, tb / 2); + } + else + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Current mode: VSYNC ON"), tr, tg, tb); + } } } break; } case Menu::audiooptions: - switch (game.currentmenuoption) + if (key.using_touch) { - case 0: - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Music Volume"), tr, tg, tb); - font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change the volume of the music."), tr, tg, tb); - volumesliderrender(); - break; - case 1: - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Sound Volume"), tr, tg, tb); - font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change the volume of sound effects."), tr, tg, tb); - volumesliderrender(); - break; - case 2: - { - if (!music.mmmmmm) + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Audio Options"), tr, tg, tb); + if (music.mmmmmm) { - break; + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Adjust volume settings and soundtrack."), tr, tg, tb); } - - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Soundtrack"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Toggle between MMMMMM and PPPPPP."), tr, tg, tb); - - const char* soundtrack; - if (music.usingmmmmmm) + else { - soundtrack = loc::gettext("Current soundtrack: MMMMMM"); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Adjust volume settings."), tr, tg, tb); } - else + } + else + { + switch (game.currentmenuoption) + { + case 0: + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Music Volume"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change the volume of the music."), tr, tg, tb); + volumesliderrender(); + break; + case 1: + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Sound Volume"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change the volume of sound effects."), tr, tg, tb); + volumesliderrender(); + break; + case 2: { - soundtrack = loc::gettext("Current soundtrack: PPPPPP"); + if (!music.mmmmmm) + { + break; + } + + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Soundtrack"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Toggle between MMMMMM and PPPPPP."), tr, tg, tb); + + const char* soundtrack; + if (music.usingmmmmmm) + { + soundtrack = loc::gettext("Current soundtrack: MMMMMM"); + } + else + { + soundtrack = loc::gettext("Current soundtrack: PPPPPP"); + } + font::print_wrap(PR_CEN, -1, next_y, soundtrack, tr, tg, tb); + break; } - font::print_wrap(PR_CEN, -1, next_y, soundtrack, tr, tg, tb); - break; } } @@ -763,6 +839,59 @@ static void menurender(void) break; } + case Menu::touch_input: + { + if (key.using_touch) + { + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Touch Input"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change touch input options."), tr, tg, tb); + + font::print(PR_CEN, -1, 128, loc::gettext("Current style:"), tr, tg, tb); + switch (touch::style) + { + case 0: + font::print(PR_CEN, -1, 138, loc::gettext("SWIPE"), tr, tg, tb); + break; + case 1: + font::print(PR_CEN, -1, 138, loc::gettext("D-PAD"), tr, tg, tb); + break; + } + } + else + { + switch (game.currentmenuoption) + { + case 0: // Control style + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Control Style"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change the control style for touch input."), tr, tg, tb); + + font::print(PR_CEN, -1, 88, loc::gettext("Current style:"), tr, tg, tb); + switch (touch::style) + { + case 0: + font::print(PR_CEN, -1, 98, loc::gettext("SWIPE"), tr, tg, tb); + break; + case 1: + font::print(PR_CEN, -1, 98, loc::gettext("D-PAD"), tr, tg, tb); + break; + } + break; + case 1: + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("UI Scale"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Change the scale of the UI buttons."), tr, tg, tb); + + char buffer[SCREEN_WIDTH_CHARS + 1]; + float scale = (float) touch::scale / 10.0; + vformat_buf(buffer, sizeof(buffer), loc::gettext("Current scale: {scale}.{extra}x"), "scale:int, extra:int", + (int)scale, + (int)((float)((float)scale - (int)scale) * 10) + ); + font::print(PR_CEN, -1, 75, buffer, tr, tg, tb); + break; + } + } + } + break; case Menu::language: if (loc::languagelist.empty()) { @@ -771,22 +900,25 @@ static void menurender(void) else if ((unsigned)game.currentmenuoption < loc::languagelist.size()) { font::print_wrap(PR_CEN, -1, 8, loc::languagelist[game.currentmenuoption].credit.c_str(), tr/2, tg/2, tb/2); - const char* select_hint; - char buffer[SCREEN_WIDTH_CHARS + 1]; - if (BUTTONGLYPHS_keyboard_is_active()) - { - select_hint = loc::languagelist[game.currentmenuoption].action_hint.c_str(); - } - else + if (!key.using_touch) { - vformat_buf(buffer, sizeof(buffer), - loc::languagelist[game.currentmenuoption].gamepad_hint.c_str(), - "button:but", - vformat_button(ActionSet_Menu, Action_Menu_Accept) - ); - select_hint = buffer; + const char* select_hint; + char buffer[SCREEN_WIDTH_CHARS + 1]; + if (BUTTONGLYPHS_keyboard_is_active()) + { + select_hint = loc::languagelist[game.currentmenuoption].action_hint.c_str(); + } + else + { + vformat_buf(buffer, sizeof(buffer), + loc::languagelist[game.currentmenuoption].gamepad_hint.c_str(), + "button:but", + vformat_button(ActionSet_Menu, Action_Menu_Accept) + ); + select_hint = buffer; + } + font::print(PR_CEN, -1, 230, select_hint, tr / 2, tg / 2, tb / 2); } - font::print(PR_CEN, -1, 230, select_hint, tr/2, tg/2, tb/2); } break; case Menu::translator_main: @@ -1166,89 +1298,97 @@ static void menurender(void) break; case Menu::accessibility: { + if (key.using_touch) + { + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Accessibility"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disable screen effects, enable slowdown modes or invincibility."), tr, tg, tb); + } + else + { #ifdef MAKEANDPLAY #define OFFSET 0 #else #define OFFSET 1 #endif - switch (game.currentmenuoption) - { + switch (game.currentmenuoption) + { #if !defined(MAKEANDPLAY) - case 0: - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Unlock Play Modes"), tr, tg, tb); - font::print_wrap(PR_CEN, -1, 65, loc::gettext("Unlock parts of the game normally unlocked as you progress."), tr, tg, tb); - break; + case 0: + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Unlock Play Modes"), tr, tg, tb); + font::print_wrap(PR_CEN, -1, 65, loc::gettext("Unlock parts of the game normally unlocked as you progress."), tr, tg, tb); + break; #endif - case OFFSET+0: - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Invincibility"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Explore the game freely without dying. (Can cause glitches.)"), tr, tg, tb); - if (map.invincibility) + case OFFSET + 0: { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Invincibility is ON."), tr, tg, tb); - } - else - { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Invincibility is OFF."), tr / 2, tg / 2, tb / 2); + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Invincibility"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Explore the game freely without dying. (Can cause glitches.)"), tr, tg, tb); + if (map.invincibility) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Invincibility is ON."), tr, tg, tb); + } + else + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Invincibility is OFF."), tr / 2, tg / 2, tb / 2); + } + break; } - break; - } - case OFFSET+1: - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Slowdown"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Reduce the game speed."), tr, tg, tb); - drawslowdowntext(next_y); - break; - } - case OFFSET+2: - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Backgrounds"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disable animated backgrounds in menus and during gameplay."), tr, tg, tb); - if (!game.colourblindmode) + case OFFSET + 1: { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Backgrounds are ON."), tr, tg, tb); + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Slowdown"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Reduce the game speed."), tr, tg, tb); + drawslowdowntext(next_y); + break; } - else + case OFFSET + 2: { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Backgrounds are OFF."), tr / 2, tg / 2, tb / 2); + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Backgrounds"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disable animated backgrounds in menus and during gameplay."), tr, tg, tb); + if (!game.colourblindmode) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Backgrounds are ON."), tr, tg, tb); + } + else + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Backgrounds are OFF."), tr / 2, tg / 2, tb / 2); + } + break; } - break; - } - case OFFSET+3: - { - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Screen Effects"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disables screen shakes and flashes."), tr, tg, tb); - if (!game.noflashingmode) + case OFFSET + 3: { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Screen Effects are ON."), tr, tg, tb); + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Screen Effects"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disables screen shakes and flashes."), tr, tg, tb); + if (!game.noflashingmode) + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Screen Effects are ON."), tr, tg, tb); + } + else + { + font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Screen Effects are OFF."), tr / 2, tg / 2, tb / 2); + } + break; } - else + case OFFSET + 4: { - font::print_wrap(PR_CEN, -1, next_y, loc::gettext("Screen Effects are OFF."), tr / 2, tg / 2, tb / 2); - } - break; - } - case OFFSET+4: - { - const char* text; + const char* text; - font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Text Outline"), tr, tg, tb); - int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disables outline on game text."), tr, tg, tb); + font::print(PR_2X | PR_CEN, -1, 30, loc::gettext("Text Outline"), tr, tg, tb); + int next_y = font::print_wrap(PR_CEN, -1, 65, loc::gettext("Disables outline on game text."), tr, tg, tb); - graphics.fill_rect(0, next_y-4, 320, 16, tr, tg, tb); + graphics.fill_rect(0, next_y - 4, 320, 16, tr, tg, tb); - if (!graphics.notextoutline) - { - text = loc::gettext("Text outlines are ON."); - } - else - { - text = loc::gettext("Text outlines are OFF."); - } + if (!graphics.notextoutline) + { + text = loc::gettext("Text outlines are ON."); + } + else + { + text = loc::gettext("Text outlines are OFF."); + } - font::print(PR_BOR | PR_CEN, -1, next_y, text, 255, 255, 255); - break; + font::print(PR_BOR | PR_CEN, -1, next_y, text, 255, 255, 255); + break; + } } } @@ -1330,52 +1470,37 @@ static void menurender(void) { const char* title = NULL; struct Game::Summary* summary = NULL; + struct Game::Summary* telesummary = &game.last_telesave; + struct Game::Summary* quicksummary = &game.last_quicksave; switch (game.currentmenuoption) { case 0: title = loc::gettext("Tele Save"); - summary = &game.last_telesave; + summary = telesummary; break; case 1: title = loc::gettext("Quick Save"); - summary = &game.last_quicksave; + summary = quicksummary; break; } - if (summary != NULL) + if (key.using_touch) { - graphics.drawpixeltextbox(17, 65-20, 286, 90, 65, 185, 207); + dont_render_buttons = true; + touch::render_buttons(tr, tg, tb, true); - font::print(PR_2X | PR_CEN, -1, 20, title, tr, tg, tb); - font::print( - PR_CEN, -1, 80-20, - loc::gettext_roomname_special(map.currentarea(summary->saverx, summary->savery)), - 25, 255 - (help.glow / 2), 255 - (help.glow / 2) - ); - for (int i = 0; i < 6; i++) - { - graphics.drawcrewman(169-(3*42)+(i*42), 95-20, i, summary->crewstats[i], true); - } - font::print( - 0, 59, 132-20, - game.giventimestring( - summary->hours, - summary->minutes, - summary->seconds - ), - 255 - (help.glow / 2), 255 - (help.glow / 2), 255 - (help.glow / 2) - ); - char buffer[SCREEN_WIDTH_CHARS + 1]; - vformat_buf(buffer, sizeof(buffer), - loc::gettext("{savebox_n_trinkets|wordy}"), - "savebox_n_trinkets:int", - summary->trinkets - ); - font::print(PR_RIGHT, 262, 132-20, buffer, 255 - (help.glow / 2), 255 - (help.glow / 2), 255 - (help.glow / 2)); + draw_summary(telesummary, -32 + 6); + draw_summary(quicksummary, 64 + 6); - graphics.draw_sprite(34, 126-20, 50, graphics.col_clock); - graphics.draw_sprite(270, 126-20, 22, graphics.col_trinket); + font::print(PR_CEN | PR_BOR, -1, 32 - 16 + 2, loc::gettext("Tele Save"), 196, 196, 255 - help.glow); + font::print(PR_CEN | PR_BOR, -1, 128 - 16 + 2, loc::gettext("Quick Save"), 196, 196, 255 - help.glow); + } + else if (summary != NULL) + { + graphics.drawpixeltextbox(17, 65 - 20, 286, 90, 65, 185, 207); + font::print(PR_2X | PR_CEN, -1, 20, title, tr, tg, tb); + draw_summary(summary, 0); } break; } @@ -1867,6 +1992,7 @@ static void menurender(void) void titlerender(void) { + dont_render_buttons = false; graphics.clear(); if (!game.menustart) { @@ -1917,7 +2043,18 @@ 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) + { + if (!dont_render_buttons) + { + touch::render_buttons(tr, tg, tb); + } + } + else + { + graphics.drawmenu(tr, tg, tb, game.currentmenuname); + } } graphics.drawfade(); @@ -2329,7 +2466,7 @@ void gamerender(void) } graphics.drawtowermap(); } - else + else if (!game.tutorial_mode) { if(!game.colourblindmode) { @@ -2348,7 +2485,10 @@ void gamerender(void) graphics.drawmap(); } } - + else if (game.tutorial_mode) + { + graphics.fill_rect(10, 24, 26); + } graphics.drawentities(); if (map.towermode) @@ -2460,12 +2600,19 @@ void gamerender(void) if (game.advancetext) { char buffer_adv[SCREEN_WIDTH_CHARS + 1]; - vformat_buf( - buffer_adv, sizeof(buffer_adv), - loc::gettext("- Press {button} to advance text -"), - "button:but", - vformat_button(ActionSet_InGame, Action_InGame_ACTION) - ); + if (key.using_touch) + { + SDL_strlcpy(buffer_adv, loc::gettext("- Tap screen to advance text -"), sizeof(buffer_adv)); + } + else + { + vformat_buf( + buffer_adv, sizeof(buffer_adv), + loc::gettext("- Press {button} to advance text -"), + "button:but", + vformat_button(ActionSet_InGame, Action_InGame_ACTION) + ); + } font::print(PR_CEN | PR_BOR, -1, graphics.flipmode ? 228 : 5, buffer_adv, 220 - (help.glow), 220 - (help.glow), 255 - (help.glow / 2)); } @@ -2762,6 +2909,117 @@ void gamerender(void) graphics.drawtrophytext(); } + if (game.tutorial_mode) + { + int screen_off = (100 - game.tutorial_screen_pos) * 3; + + font::print_wrap(PR_CEN, -1, 10, loc::gettext("-= TOUCHSCREEN CONTROLS =-"), 200 - help.glow, 220 - help.glow, 255 - help.glow / 2); + + if (game.tutorial_state >= 3 && game.tutorial_state <= 6) + { + font::print_wrap(PR_CEN, -1, 203, "Swipe and hold on the left side of the screen to move", 200 - help.glow, 220 - help.glow, 255 - help.glow / 2, -1, SCREEN_WIDTH_PIXELS / 3 * 2); + } + + if (game.tutorial_state >= 7 && game.tutorial_state <= 11) + { + font::print_wrap(PR_CEN, -1, 203, "Tap on the right to flip", 200 - help.glow, 220 - help.glow, 255 - help.glow / 2, -1, SCREEN_WIDTH_PIXELS / 3 * 2); + } + + if (game.tutorial_state >= 13) + { + font::print_wrap(PR_CEN, -1, 203, "Tap to start", 200 - help.glow, 220 - help.glow, 255 - help.glow / 2, -1, SCREEN_WIDTH_PIXELS / 3 * 2); + } + + graphics.draw_texture(graphics.grphx.im_tutorial_screen, 80, 70 + screen_off); + + // Touchpoints! + if (!game.press_left && !game.press_right) + { + // Left hand off + graphics.draw_texture(graphics.grphx.im_tutorial_lefthand_off, 56, 106 + screen_off); + } + else if (game.press_left) + { + // Left hand moving left + if (game.tutorial_touch_timer <= 8) + { + if (help.slowsine % 16 > 8) + { + graphics.draw_rect(116, 120, 16, 16, 255, 255, 255); + } + else + { + graphics.draw_rect(118, 122, 12, 12, 255, 255, 255); + } + graphics.draw_texture(graphics.grphx.im_tutorial_lefthand_far, 56, 106 + screen_off); + graphics.draw_texture(graphics.grphx.im_tutorial_arrowleft, 136, 120); + } + else + { + if (help.slowsine % 16 > 8) + { + graphics.draw_rect(104, 118, 16, 16, 255, 255, 255); + } + else + { + graphics.draw_rect(106, 120, 12, 12, 255, 255, 255); + } + graphics.draw_texture(graphics.grphx.im_tutorial_lefthand_near, 56, 106 + screen_off); + graphics.draw_texture(graphics.grphx.im_tutorial_arrowleft, 124, 120); + } + } + else if (game.press_right) + { + // Left hand moving right + if (game.tutorial_touch_timer <= 8) + { + if (help.slowsine % 16 > 8) + { + graphics.draw_rect(104, 118, 16, 16, 255, 255, 255); + } + else + { + graphics.draw_rect(106, 120, 12, 12, 255, 255, 255); + } + graphics.draw_texture(graphics.grphx.im_tutorial_lefthand_near, 56, 106 + screen_off); + graphics.draw_texture(graphics.grphx.im_tutorial_arrowright, 124, 120); + } + else + { + if (help.slowsine % 16 > 8) + { + graphics.draw_rect(116, 120, 16, 16, 255, 255, 255); + } + else + { + graphics.draw_rect(118, 122, 12, 12, 255, 255, 255); + } + graphics.draw_texture(graphics.grphx.im_tutorial_lefthand_far, 56, 106 + screen_off); + graphics.draw_texture(graphics.grphx.im_tutorial_arrowright, 136, 120); + } + } + + if (game.tutorial_state >= 7) + { + if (game.tutorial_flip > 0) + { + if (help.slowsine % 16 > 8) + { + graphics.draw_rect(188, 120, 16, 16, 255, 255, 255); + } + else + { + graphics.draw_rect(190, 122, 12, 12, 255, 255, 255); + } + graphics.draw_texture(graphics.grphx.im_tutorial_righthand_far, 193, 106); + } + else + { + graphics.draw_texture(graphics.grphx.im_tutorial_righthand_off, 193, 106 + screen_off); + } + } + } + level_debugger::render(); graphics.renderwithscreeneffects(); @@ -3099,14 +3357,17 @@ void maprender(void) } else if (obj.flags[67] && !map.custommode) { - char buffer[SCREEN_WIDTH_CHARS + 1]; - vformat_buf( - buffer, sizeof(buffer), - loc::gettext("Press {button} to warp to the ship."), - "button:but", - vformat_button(ActionSet_InGame, Action_InGame_ACTION) - ); - font::print_wrap(PR_CEN, -1, 105, buffer, 196, 196, 255 - help.glow); + if (!key.using_touch) + { + char buffer[SCREEN_WIDTH_CHARS + 1]; + vformat_buf( + buffer, sizeof(buffer), + loc::gettext("Press {button} to warp to the ship."), + "button:but", + vformat_button(ActionSet_InGame, Action_InGame_ACTION) + ); + font::print_wrap(PR_CEN, -1, 105, buffer, 196, 196, 255 - help.glow); + } } else if(map.custommode){ LevelMetaData& meta = cl.ListOfMetaData[game.playcustomlevel]; @@ -3255,7 +3516,7 @@ void maprender(void) /* We are not in a special case, so draw the save screen now... */ - if (!map.custommode) + if (!map.custommode && ((!game.gamesaved && key.using_touch) || !key.using_touch)) { /* FIXME: The text here should be automatically "balance-wrapped" instead of hardcoding the width. * In fact, maybe print_wrap should balance-wrap by default. */ @@ -3265,21 +3526,24 @@ void maprender(void) if (!game.gamesaved) { char buffer[SCREEN_WIDTH_CHARS + 1]; - vformat_buf( - buffer, sizeof(buffer), - loc::gettext("[Press {button} to save your game]"), - "button:but", - vformat_button(ActionSet_InGame, Action_InGame_ACTION) - ); + if (!key.using_touch) + { + vformat_buf( + buffer, sizeof(buffer), + loc::gettext("[Press {button} to save your game]"), + "button:but", + vformat_button(ActionSet_InGame, Action_InGame_ACTION) + ); - font::print(PR_CEN, -1, 80, buffer, 255 - help.glow*2, 255 - help.glow*2, 255 - help.glow); + font::print(PR_CEN, -1, 80, buffer, 255 - help.glow * 2, 255 - help.glow * 2, 255 - help.glow); + } if (map.custommode || !game.last_quicksave.exists) { break; } - font::print(PR_CEN, -1, FLIP(100, 8), loc::gettext("Last Save:"), 164 - help.glow/4, 164 - help.glow/4, 164); + font::print(PR_CEN, -1, FLIP((key.using_touch ? 40 : 100), 8), loc::gettext("Last Save:"), 164 - help.glow / 4, 164 - help.glow / 4, 164); struct Game::Summary* last = &game.last_quicksave; vformat_buf( @@ -3290,7 +3554,7 @@ void maprender(void) game.giventimestring(last->hours, last->minutes, last->seconds).c_str() ); - font::print(PR_CEN, -1, FLIP(112, 8), buffer, 164 - help.glow/4, 164 - help.glow/4, 164); + font::print(PR_CEN, -1, FLIP((key.using_touch ? 52 : 112), 8), buffer, 164 - help.glow/4, 164 - help.glow/4, 164); break; } @@ -3355,8 +3619,11 @@ void maprender(void) font::print_wrap(PR_CEN, -1, 142, loc::gettext("Do you want to quit? You will lose any unsaved progress."), 196, 196, 255 - help.glow, 12); } - font::print(PR_RTL_XFLIP, 80-selection_offset, 88, loc::gettext("[ NO, KEEP PLAYING ]"), 196, 196, 255 - help.glow); - font::print(PR_RTL_XFLIP, 80 + 32, 76, loc::gettext("yes, quit to menu"), 96, 96, 96); + if (!key.using_touch) + { + font::print(PR_RTL_XFLIP, 80 - selection_offset, 88, loc::gettext("[ NO, KEEP PLAYING ]"), 196, 196, 255 - help.glow); + font::print(PR_RTL_XFLIP, 80 + 32, 76, loc::gettext("yes, quit to menu"), 96, 96, 96); + } } else { @@ -3370,8 +3637,11 @@ void maprender(void) font::print_wrap(PR_CEN, -1, 76, loc::gettext("Do you want to quit? You will lose any unsaved progress."), 196, 196, 255 - help.glow, 12); } - font::print(PR_RTL_XFLIP, 80-selection_offset, 130, loc::gettext("[ NO, KEEP PLAYING ]"), 196, 196, 255 - help.glow); - font::print(PR_RTL_XFLIP, 80 + 32, 142, loc::gettext("yes, quit to menu"), 96, 96, 96); + if (!key.using_touch) + { + font::print(PR_RTL_XFLIP, 80 - selection_offset, 130, loc::gettext("[ NO, KEEP PLAYING ]"), 196, 196, 255 - help.glow); + font::print(PR_RTL_XFLIP, 80 + 32, 142, loc::gettext("yes, quit to menu"), 96, 96, 96); + } } break; @@ -3389,8 +3659,11 @@ void maprender(void) font::print_wrap(PR_CEN, -1, 142, loc::gettext("Do you want to quit? You will lose any unsaved progress."), 196, 196, 255 - help.glow, 12); } - font::print(PR_RTL_XFLIP, 80, 88, loc::gettext("no, keep playing"), 96,96,96); - font::print(PR_RTL_XFLIP, 80+32-selection_offset, 76, loc::gettext("[ YES, QUIT TO MENU ]"), 196, 196, 255 - help.glow); + if (!key.using_touch) + { + font::print(PR_RTL_XFLIP, 80, 88, loc::gettext("no, keep playing"), 96, 96, 96); + font::print(PR_RTL_XFLIP, 80 + 32 - selection_offset, 76, loc::gettext("[ YES, QUIT TO MENU ]"), 196, 196, 255 - help.glow); + } } else { @@ -3403,8 +3676,11 @@ void maprender(void) font::print_wrap(PR_CEN, -1, 76, loc::gettext("Do you want to quit? You will lose any unsaved progress."), 196, 196, 255 - help.glow, 12); } - font::print(PR_RTL_XFLIP, 80, 130, loc::gettext("no, keep playing"), 96,96,96); - font::print(PR_RTL_XFLIP, 80+32-selection_offset, 142, loc::gettext("[ YES, QUIT TO MENU ]"), 196, 196, 255 - help.glow); + if (!key.using_touch) + { + font::print(PR_RTL_XFLIP, 80, 130, loc::gettext("no, keep playing"), 96, 96, 96); + font::print(PR_RTL_XFLIP, 80 + 32 - selection_offset, 142, loc::gettext("[ YES, QUIT TO MENU ]"), 196, 196, 255 - help.glow); + } } break; case 20: @@ -3413,14 +3689,20 @@ void maprender(void) if (graphics.flipmode) { font::print_wrap(PR_CEN, -1, 88, loc::gettext("Do you want to return to the secret laboratory?"), 196, 196, 255 - help.glow, 12); - font::print(PR_RTL_XFLIP, 80-selection_offset, 142, loc::gettext("[ NO, KEEP PLAYING ]"), 196, 196, 255 - help.glow); - font::print(PR_RTL_XFLIP, 80 + 32, 130, loc::gettext("yes, return"), 96, 96, 96); + if (!key.using_touch) + { + font::print(PR_RTL_XFLIP, 80 - selection_offset, 142, loc::gettext("[ NO, KEEP PLAYING ]"), 196, 196, 255 - help.glow); + font::print(PR_RTL_XFLIP, 80 + 32, 130, loc::gettext("yes, return"), 96, 96, 96); + } } else { font::print_wrap(PR_CEN, -1, 76, loc::gettext("Do you want to return to the secret laboratory?"), 196, 196, 255 - help.glow, 12); - font::print(PR_RTL_XFLIP, 80-selection_offset, 130, loc::gettext("[ NO, KEEP PLAYING ]"), 196, 196, 255 - help.glow); - font::print(PR_RTL_XFLIP, 80 + 32, 142, loc::gettext("yes, return"), 96, 96, 96); + if (!key.using_touch) + { + font::print(PR_RTL_XFLIP, 80 - selection_offset, 130, loc::gettext("[ NO, KEEP PLAYING ]"), 196, 196, 255 - help.glow); + font::print(PR_RTL_XFLIP, 80 + 32, 142, loc::gettext("yes, return"), 96, 96, 96); + } } break; @@ -3430,18 +3712,26 @@ void maprender(void) if (graphics.flipmode) { font::print_wrap(PR_CEN, -1, 88, loc::gettext("Do you want to return to the secret laboratory?"), 196, 196, 255 - help.glow, 12); - font::print(PR_RTL_XFLIP, 80, 142, loc::gettext("no, keep playing"), 96, 96, 96); - font::print(PR_RTL_XFLIP, 80 + 32-selection_offset, 130, loc::gettext("[ YES, RETURN ]"), 196, 196, 255 - help.glow); + if (!key.using_touch) + { + font::print(PR_RTL_XFLIP, 80, 142, loc::gettext("no, keep playing"), 96, 96, 96); + font::print(PR_RTL_XFLIP, 80 + 32 - selection_offset, 130, loc::gettext("[ YES, RETURN ]"), 196, 196, 255 - help.glow); + } } else { font::print_wrap(PR_CEN, -1, 76, loc::gettext("Do you want to return to the secret laboratory?"), 196, 196, 255 - help.glow, 12); - font::print(PR_RTL_XFLIP, 80, 130, loc::gettext("no, keep playing"), 96, 96, 96); - font::print(PR_RTL_XFLIP, 80 + 32-selection_offset, 142, loc::gettext("[ YES, RETURN ]"), 196, 196, 255 - help.glow); + if (!key.using_touch) + { + font::print(PR_RTL_XFLIP, 80, 130, loc::gettext("no, keep playing"), 96, 96, 96); + font::print(PR_RTL_XFLIP, 80 + 32 - selection_offset, 142, loc::gettext("[ YES, RETURN ]"), 196, 196, 255 - help.glow); + } } } + touch::render_buttons(); + graphics.set_render_target(graphics.gameTexture); if (graphics.resumegamemode || graphics.menuoffset > 0 || graphics.oldmenuoffset > 0) @@ -3535,12 +3825,19 @@ void teleporterrender(void) if (game.advancetext) { char buffer_adv[SCREEN_WIDTH_CHARS + 1]; - vformat_buf( - buffer_adv, sizeof(buffer_adv), - loc::gettext("- Press {button} to advance text -"), - "button:but", - vformat_button(ActionSet_InGame, Action_InGame_ACTION) - ); + if (key.using_touch) + { + SDL_strlcpy(buffer_adv, loc::gettext("- Tap screen to advance text -"), sizeof(buffer_adv)); + } + else + { + vformat_buf( + buffer_adv, sizeof(buffer_adv), + loc::gettext("- Press {button} to advance text -"), + "button:but", + vformat_button(ActionSet_InGame, Action_InGame_ACTION) + ); + } font::print(PR_CEN | PR_BOR, -1, graphics.flipmode ? 228 : 5, buffer_adv, 220 - (help.glow), 220 - (help.glow), 255 - (help.glow / 2)); } diff --git a/desktop_version/src/RenderFixed.cpp b/desktop_version/src/RenderFixed.cpp index c68f484d68..73eb8a67d7 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 7c00556774..5298e15f1d 100644 --- a/desktop_version/src/Script.cpp +++ b/desktop_version/src/Script.cpp @@ -20,6 +20,7 @@ #include "LocalizationStorage.h" #include "Map.h" #include "Music.h" +#include "Touch.h" #include "Unreachable.h" #include "UtilityClass.h" #include "VFormat.h" @@ -197,10 +198,13 @@ void scriptclass::run(void) tokenize(commands[position]); //For script assisted input - game.press_left = false; - game.press_right = false; - game.press_action = false; - game.press_map = false; + if (!game.tutorial_mode) + { + game.press_left = false; + game.press_right = false; + game.press_action = false; + game.press_map = false; + } //Ok, now we run a command based on that string if (words[0] == "moveplayer") @@ -802,7 +806,7 @@ void scriptclass::run(void) game.hascontrol = false; game.pausescript = true; if (key.isDown(90) || key.isDown(32) || key.isDown(86) - || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN)) game.jumpheld = true; + || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || touch::screen_down()) game.jumpheld = true; } game.backgroundtext = false; @@ -1417,6 +1421,11 @@ void scriptclass::run(void) graphics.setfade(0); graphics.fademode = FADE_NONE; } + else if (words[0] == "befadeout") + { + graphics.setfade(416); + graphics.fademode = FADE_FULLY_BLACK; + } else if (words[0] == "fadein") { graphics.fademode = FADE_START_FADEIN; @@ -1852,7 +1861,7 @@ void scriptclass::run(void) game.hascontrol = false; game.pausescript = true; if (key.isDown(90) || key.isDown(32) || key.isDown(86) - || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN)) game.jumpheld = true; + || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || touch::screen_down()) game.jumpheld = true; } game.backgroundtext = false; } @@ -1875,7 +1884,7 @@ void scriptclass::run(void) game.hascontrol = false; game.pausescript = true; if (key.isDown(90) || key.isDown(32) || key.isDown(86) - || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN)) game.jumpheld = true; + || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || touch::screen_down()) game.jumpheld = true; } game.backgroundtext = false; } @@ -1896,7 +1905,7 @@ void scriptclass::run(void) game.hascontrol = false; game.pausescript = true; if (key.isDown(90) || key.isDown(32) || key.isDown(86) - || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN)) game.jumpheld = true; + || key.isDown(KEYBOARD_UP) || key.isDown(KEYBOARD_DOWN) || touch::screen_down()) game.jumpheld = true; } game.backgroundtext = false; } @@ -2513,6 +2522,44 @@ void scriptclass::run(void) } } } + else if (words[0] == "settile") + { + map.settile(ss_toi(words[1]), ss_toi(words[2]), ss_toi(words[3])); + graphics.foregrounddrawn = false; + } + else if (words[0] == "controls") + { + if (!game.seen_touch_tutorial && touch::style == TOUCH_STYLE_SWIPE) + { + game.tutorial_mode = true; + game.tutorial_state = 0; + game.tutorial_timer = 0; + + game.tutorial_screen_pos = 0; + game.tutorial_touch_timer = 0; + game.tutorial_flip = 0; + } + } + else if (words[0] == "untilcontrols") + { + if (!game.seen_touch_tutorial) + { + if (game.tutorial_mode) + { + scriptdelay = 1; + position--; + } + else + { + game.seen_touch_tutorial = true; + game.savesettings(); + } + } + } + else if (words[0] == "setbars") + { + graphics.setbars(ss_toi(words[1])); + } position++; } @@ -2594,6 +2641,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 */ @@ -3220,6 +3269,14 @@ void scriptclass::hardreset(void) game.ingame_titlemode = false; + game.tutorial_mode = false; + game.tutorial_state = 0; + game.tutorial_timer = 0; + + game.tutorial_screen_pos = 0; + game.tutorial_touch_timer = 0; + game.tutorial_flip = 0; + //dwgraphicsclass graphics.backgrounddrawn = false; graphics.textboxes.clear(); diff --git a/desktop_version/src/Scripts.cpp b/desktop_version/src/Scripts.cpp index 18f1984cad..bd45f56dbe 100644 --- a/desktop_version/src/Scripts.cpp +++ b/desktop_version/src/Scripts.cpp @@ -188,14 +188,53 @@ bool scriptclass::load(const std::string& name) "changemood(player,0)", "changedir(player,1)", - "delay(100)", + //"delay(100)", + "delay(5)", + "gotoroom(7,10)", + "gotoposition(148,32,0)", + "warpdir(8,11,3)", + "settile(25,8,495)", + "settile(26,8,495)", + "settile(34,8,495)", + "settile(35,8,495)", + "settile(36,4,0)", + "settile(37,4,0)", + "settile(38,4,0)", + "settile(39,4,0)", + "settile(36,5,0)", + "settile(37,5,0)", + "settile(38,5,0)", + "settile(39,5,0)", + "settile(36,6,0)", + "settile(37,6,0)", + "settile(38,6,0)", + "settile(39,6,0)", + "settile(36,7,0)", + "settile(37,7,0)", + "settile(38,7,0)", + "settile(39,7,0)", + "delay(50)", "blackon()", + "endcutscene()", + "setbars(0)", + "changemood(player,0)", + "changedir(player,0)", + "controls()", + "untilcontrols()", + "befadeout()", + "cutscene()", + "setbars(320)", + "delay(45)", // 100 frames total + + "befadein()", "shake(20)", "playef(10)", //Finally, appear at the start of the game: "gotoroom(13,5)", "gotoposition(80,96,0)", + "hidecoordinates(7,10)", // Hide coordinates where the touch tutorial took place + "warpdir(8,11,0)", "walk(right,20)", //"delay(45)", diff --git a/desktop_version/src/Touch.cpp b/desktop_version/src/Touch.cpp new file mode 100644 index 0000000000..ab2ac2c2e2 --- /dev/null +++ b/desktop_version/src/Touch.cpp @@ -0,0 +1,878 @@ +#include "Touch.h" + +#include +#include +#include + +#include "ButtonGlyphs.h" +#include "Constants.h" +#include "CustomLevels.h" +#include "Editor.h" +#include "Entity.h" +#include "FileSystemUtils.h" +#include "Font.h" +#include "Game.h" +#include "GlitchrunnerMode.h" +#include "Graphics.h" +#include "GraphicsResources.h" +#include "Input.h" +#include "Localization.h" +#include "KeyPoll.h" +#include "Map.h" +#include "Music.h" +#include "Screen.h" +#include "Script.h" +#include "UtilityClass.h" + +namespace touch +{ + std::vector fingers; + TouchButton buttons[NUM_TOUCH_BUTTONS]; + std::vector dynamic_buttons; + std::vector all_buttons; + bool use_buttons; + int scale; + bool textbox_style; + bool scroll; + TouchControlStyle style; + SDL_FingerID swipe_finger; + int swipe_x; + int swipe_delta; + + void refresh_all_buttons(void) + { + all_buttons.clear(); + + for (int i = 0; i < NUM_TOUCH_BUTTONS; i++) + { + all_buttons.push_back(&buttons[i]); + } + + for (int i = 0; i < dynamic_buttons.size(); i++) + { + all_buttons.push_back(&dynamic_buttons[i]); + } + } + + int get_rect(TouchButton* button, SDL_Rect* rect) + { + rect->x = button->x; + rect->y = button->y - scroll; + rect->w = button->width; + rect->h = button->height; + + return 0; + } + + int get_scale(void) + { + SDL_Rect rect; + graphics.get_stretch_info(&rect); + + int scale_x = rect.w / SCREEN_WIDTH_PIXELS; + int scale_y = rect.h / SCREEN_HEIGHT_PIXELS; + + return SDL_ceil(SDL_min(scale_x, scale_y) * ((float) scale / 10.f)); + } + + void init(void) + { + scale = 10; + use_buttons = false; + textbox_style = false; + + swipe_x = 0; + swipe_delta = 0; + swipe_finger = -1; + + for (int i = 0; i < NUM_TOUCH_BUTTONS; i++) + { + buttons[i].image = NULL; + buttons[i].text = ""; + buttons[i].active = false; + buttons[i].pressed = false; + buttons[i].down = false; + buttons[i].fingerId = -1; + buttons[i].core = true; + buttons[i].ui = true; + buttons[i].type = TOUCH_BUTTON_TYPE_NONE; + buttons[i].id = -1; + buttons[i].disabled = false; + buttons[i].checked = false; + buttons[i].flags = 0; + } + + refresh_all_buttons(); + } + + TouchButton create_button(int x, int y, int width, int height, std::string text) + { + 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; + button.checked = false; + button.flags = 0; + + 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_flags(int x, int y, int width, int height, std::string text, int id, Uint8 flags) + { + TouchButton button = create_button(x, y, width, height, text); + button.type = TOUCH_BUTTON_TYPE_MENU; + button.id = id; + button.flags = flags; + + 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 create_slider_button(int x, int y, int width, int height, std::string text, int* var, int minvalue, int maxvalue) + { + TouchButton button = create_button(x, y, width, height, text); + button.type = TOUCH_BUTTON_TYPE_MENU_SLIDER; + button.id = -1; + button.disabled = false; + button.min = minvalue; + button.max = maxvalue; + button.var = var; + + register_button(button); + } + + void create_toggle_button(int x, int y, int width, int height, std::string text, int id, bool checked) + { + TouchButton button = create_button(x, y, width, height, text); + button.type = TOUCH_BUTTON_TYPE_MENU_TOGGLE; + button.id = id; + button.checked = checked; + + 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_TOGGLE: + button->checked = !button->checked; + SDL_FALLTHROUGH; + case TOUCH_BUTTON_TYPE_MENU: + case TOUCH_BUTTON_TYPE_MENU_LANGUAGE: + game.currentmenuoption = button->id; + menuactionpress(); + break; + case TOUCH_BUTTON_TYPE_MAP: + switch (button->id) + { + case 0: + case 1: + case 2: + case 3: + game.menupage = button->id; + music.playef(Sound_VIRIDIAN); + break; + case 4: + game.menupage = 1; + mapmenuactionpress(version2_2); + break; + case 5: + game.menupage = 3; + mapmenuactionpress(version2_2); + break; + case 6: + music.playef(Sound_VIRIDIAN); + game.menupage = 10; + break; + case 7: + music.playef(Sound_VIRIDIAN); + game.menupage = 3; + break; + case 8: + game.menupage = 11; + mapmenuactionpress(version2_2); + break; + case 9: + game.menupage = 20; + mapmenuactionpress(version2_2); + break; + case 10: + game.menupage = 21; + mapmenuactionpress(version2_2); + break; + } + break; + case TOUCH_BUTTON_TYPE_NONE: + case TOUCH_BUTTON_TYPE_MENU_SLIDER: + default: + break; + } + refresh_buttons(); + } + + + static void setup_map_buttons(void) + { + buttons[TOUCH_BUTTON_MAP_MAP].x = 16; + buttons[TOUCH_BUTTON_MAP_MAP].y = 211; + buttons[TOUCH_BUTTON_MAP_MAP].width = 56; + buttons[TOUCH_BUTTON_MAP_MAP].height = 26; + buttons[TOUCH_BUTTON_MAP_MAP].text = loc::gettext("MAP"); + buttons[TOUCH_BUTTON_MAP_MAP].id = 0; + buttons[TOUCH_BUTTON_MAP_MAP].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_MAP_MAP].ui = false; + + const char* tab_name; + if (game.insecretlab) + { + tab_name = loc::gettext("GRAV"); + } + else if (obj.flags[67] && !map.custommode) + { + tab_name = loc::gettext("SHIP"); + } + else + { + tab_name = loc::gettext("CREW"); + } + + buttons[TOUCH_BUTTON_MAP_CREW].x = 92; + buttons[TOUCH_BUTTON_MAP_CREW].y = 211; + buttons[TOUCH_BUTTON_MAP_CREW].width = 56; + buttons[TOUCH_BUTTON_MAP_CREW].height = 26; + buttons[TOUCH_BUTTON_MAP_CREW].text = tab_name; + buttons[TOUCH_BUTTON_MAP_CREW].id = 1; + buttons[TOUCH_BUTTON_MAP_CREW].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_MAP_CREW].ui = false; + + buttons[TOUCH_BUTTON_MAP_STATS].x = 168; + buttons[TOUCH_BUTTON_MAP_STATS].y = 211; + buttons[TOUCH_BUTTON_MAP_STATS].width = 56; + buttons[TOUCH_BUTTON_MAP_STATS].height = 26; + buttons[TOUCH_BUTTON_MAP_STATS].text = loc::gettext("STATS"); + buttons[TOUCH_BUTTON_MAP_STATS].id = 2; + buttons[TOUCH_BUTTON_MAP_STATS].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_MAP_STATS].ui = false; + + buttons[TOUCH_BUTTON_MAP_QUIT].x = 244; + buttons[TOUCH_BUTTON_MAP_QUIT].y = 211; + buttons[TOUCH_BUTTON_MAP_QUIT].width = 56; + buttons[TOUCH_BUTTON_MAP_QUIT].height = 26; + buttons[TOUCH_BUTTON_MAP_QUIT].text = loc::gettext("QUIT"); + buttons[TOUCH_BUTTON_MAP_QUIT].id = 3; + buttons[TOUCH_BUTTON_MAP_QUIT].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_MAP_QUIT].ui = false; + + buttons[TOUCH_BUTTON_MAP_SHIP_WARP].x = 80; + buttons[TOUCH_BUTTON_MAP_SHIP_WARP].y = 104; + buttons[TOUCH_BUTTON_MAP_SHIP_WARP].width = 160; + buttons[TOUCH_BUTTON_MAP_SHIP_WARP].height = 30; + buttons[TOUCH_BUTTON_MAP_SHIP_WARP].text = loc::gettext("warp to ship"); + buttons[TOUCH_BUTTON_MAP_SHIP_WARP].id = 4; + buttons[TOUCH_BUTTON_MAP_SHIP_WARP].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_MAP_SHIP_WARP].ui = false; + + buttons[TOUCH_BUTTON_MAP_QUIT_SAVE].x = 80; + buttons[TOUCH_BUTTON_MAP_QUIT_SAVE].y = 80; + buttons[TOUCH_BUTTON_MAP_QUIT_SAVE].width = 160; + buttons[TOUCH_BUTTON_MAP_QUIT_SAVE].height = 26; + buttons[TOUCH_BUTTON_MAP_QUIT_SAVE].text = loc::gettext("save"); + buttons[TOUCH_BUTTON_MAP_QUIT_SAVE].id = 5; + buttons[TOUCH_BUTTON_MAP_QUIT_SAVE].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_MAP_QUIT_SAVE].ui = false; + + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].x = 80; + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].y = 112; + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].width = 160; + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].height = 26; + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].text = loc::gettext("quit"); + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].id = 6; + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].ui = false; + + buttons[TOUCH_BUTTON_SUPERGRAVITRON_NO].x = 80; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_NO].y = 128 - 16; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_NO].width = 160; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_NO].height = 26; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_NO].text = loc::gettext("no, keep playing"); + buttons[TOUCH_BUTTON_SUPERGRAVITRON_NO].id = 9; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_NO].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_NO].ui = false; + + buttons[TOUCH_BUTTON_SUPERGRAVITRON_YES].x = 80; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_YES].y = 128 + 16; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_YES].width = 160; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_YES].height = 26; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_YES].text = loc::gettext("yes, return"); + buttons[TOUCH_BUTTON_SUPERGRAVITRON_YES].id = 10; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_YES].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_YES].ui = false; + + buttons[TOUCH_BUTTON_QUIT_NO].x = 80; + buttons[TOUCH_BUTTON_QUIT_NO].y = 96 + 16; + buttons[TOUCH_BUTTON_QUIT_NO].width = 160; + buttons[TOUCH_BUTTON_QUIT_NO].height = 26; + buttons[TOUCH_BUTTON_QUIT_NO].text = loc::gettext("no, keep playing"); + buttons[TOUCH_BUTTON_QUIT_NO].id = 7; + buttons[TOUCH_BUTTON_QUIT_NO].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_QUIT_NO].ui = false; + + buttons[TOUCH_BUTTON_QUIT_YES].x = 80; + buttons[TOUCH_BUTTON_QUIT_YES].y = 96 + 32 + 16; + buttons[TOUCH_BUTTON_QUIT_YES].width = 160; + buttons[TOUCH_BUTTON_QUIT_YES].height = 26; + buttons[TOUCH_BUTTON_QUIT_YES].text = loc::gettext("yes, quit to menu"); + buttons[TOUCH_BUTTON_QUIT_YES].id = 8; + buttons[TOUCH_BUTTON_QUIT_YES].type = TOUCH_BUTTON_TYPE_MAP; + buttons[TOUCH_BUTTON_QUIT_YES].ui = false; + } + + void refresh_buttons(void) + { + int width; + int height; + int scale = get_scale(); + + gameScreen.GetScreenSize(&width, &height); + + buttons[TOUCH_BUTTON_LEFT].x = 0; + buttons[TOUCH_BUTTON_LEFT].y = height - (40 * scale) - 8; + buttons[TOUCH_BUTTON_LEFT].width = 40 * scale; + buttons[TOUCH_BUTTON_LEFT].height = 40 * scale; + buttons[TOUCH_BUTTON_LEFT].image = graphics.grphx.im_button_left; + + buttons[TOUCH_BUTTON_RIGHT].x = (40 * scale) + 8; + buttons[TOUCH_BUTTON_RIGHT].y = height - (40 * scale) - 8; + buttons[TOUCH_BUTTON_RIGHT].width = 40 * scale; + buttons[TOUCH_BUTTON_RIGHT].height = 40 * scale; + buttons[TOUCH_BUTTON_RIGHT].image = graphics.grphx.im_button_right; + + buttons[TOUCH_BUTTON_MAP].x = width - (35 * scale); + buttons[TOUCH_BUTTON_MAP].y = 0; + buttons[TOUCH_BUTTON_MAP].width = 35 * scale; + buttons[TOUCH_BUTTON_MAP].height = 30 * scale; + buttons[TOUCH_BUTTON_MAP].image = graphics.grphx.im_button_map; + + buttons[TOUCH_BUTTON_CANCEL].x = width - (40 * scale); + buttons[TOUCH_BUTTON_CANCEL].y = height - (40 * scale * 2) - 16; + buttons[TOUCH_BUTTON_CANCEL].width = 40 * scale; + buttons[TOUCH_BUTTON_CANCEL].height = 40 * scale; + buttons[TOUCH_BUTTON_CANCEL].image = graphics.grphx.im_button_left; + + buttons[TOUCH_BUTTON_CONFIRM].x = width - (40 * scale); + buttons[TOUCH_BUTTON_CONFIRM].y = height - (40 * scale) - 8; + buttons[TOUCH_BUTTON_CONFIRM].width = 40 * scale; + buttons[TOUCH_BUTTON_CONFIRM].height = 40 * scale; + buttons[TOUCH_BUTTON_CONFIRM].image = graphics.grphx.im_button_right; + + buttons[TOUCH_BUTTON_MAP_BACK].x = width - ((double)(240 - (int) graphics.lerp(graphics.oldmenuoffset, graphics.menuoffset)) / 240.0) * (60 * scale); + buttons[TOUCH_BUTTON_MAP_BACK].y = 8; + buttons[TOUCH_BUTTON_MAP_BACK].width = 60 * scale; + buttons[TOUCH_BUTTON_MAP_BACK].height = 26 * scale; + buttons[TOUCH_BUTTON_MAP_BACK].text = loc::gettext("BACK"); + + setup_map_buttons(); + + // First, reset all buttons + for (int i = 0; i < NUM_TOUCH_BUTTONS; i++) + { + buttons[i].active = false; + } + + use_buttons = true; + + // Now, set the buttons that are active + + switch (game.gamestate) + { + case GAMEMODE: + if (!script.running && game.hascontrol) + { + if (style == TOUCH_STYLE_BUTTONS) + { + buttons[TOUCH_BUTTON_LEFT].active = true; + buttons[TOUCH_BUTTON_RIGHT].active = true; + } + buttons[TOUCH_BUTTON_MAP].active = true; + } + break; + + case TITLEMODE: + if (!game.menustart) + { + use_buttons = false; + } + break; + case TELEPORTERMODE: + if (game.useteleporter) + { + buttons[TOUCH_BUTTON_LEFT].active = true; + buttons[TOUCH_BUTTON_RIGHT].active = true; + buttons[TOUCH_BUTTON_CONFIRM].active = true; + + buttons[TOUCH_BUTTON_MAP_BACK].active = true; + } + break; + case MAPMODE: + buttons[TOUCH_BUTTON_MAP_BACK].active = true; + if (game.menupage >= 0 && game.menupage < 4) + { + buttons[TOUCH_BUTTON_MAP_MAP].active = true; + buttons[TOUCH_BUTTON_MAP_CREW].active = true; + buttons[TOUCH_BUTTON_MAP_STATS].active = true; + buttons[TOUCH_BUTTON_MAP_QUIT].active = true; + } + + if (graphics.menuoffset > 0) + { + buttons[TOUCH_BUTTON_MAP_BACK].down = true; + } + + switch (game.menupage) + { + case 0: + case 2: + break; + case 1: + if (!game.insecretlab && obj.flags[67] && !map.custommode) + { + buttons[TOUCH_BUTTON_MAP_SHIP_WARP].active = true; + } + break; + case 20: + case 21: + buttons[TOUCH_BUTTON_SUPERGRAVITRON_YES].active = true; + buttons[TOUCH_BUTTON_SUPERGRAVITRON_NO].active = true; + buttons[TOUCH_BUTTON_MAP_BACK].active = false; + break; + case 3: + if (!game.gamesaved && !game.gamesavefailed && !game.inspecial()) + { + buttons[TOUCH_BUTTON_MAP_QUIT_SAVE].active = true; + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].y = 112; + } + else + { + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].y = 168; + } + buttons[TOUCH_BUTTON_MAP_QUIT_EXIT].active = true; + break; + case 10: + case 11: + buttons[TOUCH_BUTTON_QUIT_NO].active = true; + buttons[TOUCH_BUTTON_QUIT_YES].active = true; + break; + default: + buttons[TOUCH_BUTTON_LEFT].active = true; + buttons[TOUCH_BUTTON_RIGHT].active = true; + break; + } + break; + case PRELOADER: + use_buttons = false; + break; + case GAMECOMPLETE: + case GAMECOMPLETE2: + case EDITORMODE: + default: + break; + } + } + + void update_sliders() + { + SDL_Rect stretch_rect; + graphics.get_stretch_info(&stretch_rect); + + for (int i = 0; i < all_buttons.size(); i++) + { + TouchButton* button = all_buttons[i]; + + if (button->type == TOUCH_BUTTON_TYPE_MENU_SLIDER && button->pressed) + { + int value = *button->var; + int range = button->max - button->min; + float percent = (float) (value - button->min) / range; + + int finger_x = (fingers[button->fingerId].x - stretch_rect.x) * SCREEN_WIDTH_PIXELS / stretch_rect.w; + + int newvalue = button->min + (int) ((finger_x - button->x) / (float)button->width * range); + if (newvalue < button->min) + { + newvalue = button->min; + } + if (newvalue > button->max) + { + newvalue = button->max; + } + + *button->var = newvalue; + } + } + } + + void render_buttons(int scale, bool ui, int r, int g, int b) + { + for (int i = 0; i < all_buttons.size(); i++) + { + TouchButton* button = all_buttons[i]; + + if (button->active && (button->ui == ui)) + { + if (button->image != NULL) + { + graphics.draw_texture(button->image, button->x, button->y + (button->down ? 2 * scale : 0), scale, scale); + } + else + { + int use_r = button->disabled ? 127 : r; + int use_g = button->disabled ? 127 : g; + int use_b = button->disabled ? 127 : b; + + if (button->type == TOUCH_BUTTON_TYPE_MAP) + { + if (game.menupage != button->id) + { + use_r /= 2; + use_g /= 2; + use_b /= 2; + } + } + + float shadow_div = 4; + float inner_div = 1.5; + + if (textbox_style) + { + shadow_div = 6; + inner_div = 6; + } + + int offset = (button->down) ? 1 : 0; + + int font_scale = (SDL_min((scale - 1), 7) << 0); + int height = font::height(PR_CJK_LOW | font_scale | button->flags); + + switch (button->type) + { + case TOUCH_BUTTON_TYPE_MENU_SLIDER: + { + // Find where the slider position is! + int value = *button->var; + int range = button->max - button->min; + float percent = (float) (value - button->min) / range; + int sliderpos = (int) ((button->width - 10) * percent); + + // Draw track + graphics.fill_rect(button->x * scale + 2, (button->y + (button->height / 2)) * scale, button->width, 4, use_r / shadow_div, use_g / shadow_div, use_b / shadow_div); + graphics.fill_rect(button->x * scale, (button->y + (button->height / 2) - 2) * scale, button->width, 4, use_r / inner_div, use_g / inner_div, use_b / inner_div); + + // Draw slider + graphics.fill_rect((button->x + sliderpos + 2) * scale, (button->y + (button->height / 2) - 3) * scale, 10, 10, use_r / shadow_div, use_g / shadow_div, use_b / shadow_div); + graphics.fill_rect((button->x + sliderpos) * scale, (button->y + (button->height / 2) - 5) * scale, 10, 10, use_r, use_g, use_b); + graphics.fill_rect((button->x + sliderpos + 1) * scale, (button->y + (button->height / 2) - 4) * scale, 8, 8, use_r / inner_div, use_g / inner_div, use_b / inner_div); + + + font::print(PR_CEN | PR_CJK_LOW | font_scale | button->flags, button->x + (button->width / 2) * scale, button->y * scale, button->text, use_r, use_g, use_b); + break; + } + case TOUCH_BUTTON_TYPE_MENU_TOGGLE: + graphics.draw_rect(button->x + offset * scale, button->y + offset * scale, 10, 10, use_r, use_g, use_b); + if (button->checked) + { + graphics.fill_rect(button->x + 2 * scale + offset * scale, button->y + 2 * scale + offset * scale, 6, 6, use_r, use_g, use_b); + } + + font::print(PR_CJK_LOW | font_scale | button->flags, button->x + 16 + offset * scale, button->y + ((button->height - height) / 2 + offset) * scale, button->text, use_r, use_g, use_b); + break; + default: + // This is a dumb hack for the language menu, but... if this button is NOT for the current language, darken it (unless it's negative) + + if (game.currentmenuname == Menu::language) + { + if (button->id != game.currentmenuoption && button->id >= 0) + { + use_r /= 2; + use_g /= 2; + use_b /= 2; + } + } + + graphics.fill_rect(button->x + 4 * scale, button->y + 4 * scale, button->width, button->height, r / shadow_div, g / shadow_div, b / shadow_div); + + graphics.fill_rect(button->x + offset * scale, button->y + offset * scale, button->width, button->height, use_r, use_g, use_b); + graphics.fill_rect(button->x + (offset + 2) * scale, button->y + (2 + offset) * scale, button->width - 4 * scale, button->height - 4 * scale, use_r / inner_div, use_g / inner_div, use_b / inner_div); + if (button->type == TOUCH_BUTTON_TYPE_MENU_LANGUAGE) + { + graphics.set_texture_color_mod(graphics.grphx.im_button_globe, 196, 196, 255 - help.glow); + graphics.draw_texture(graphics.grphx.im_button_globe, (button->x + 4 + offset) * scale, (button->y + 4 + offset) * scale, scale, scale); + graphics.set_texture_color_mod(graphics.grphx.im_button_globe, 255, 255, 255); + } + else + { + font::print(PR_CEN | PR_CJK_LOW | font_scale | button->flags, button->x + (button->width / 2) + offset * scale, button->y + ((button->height - height) / 2 + offset * scale), button->text, 196, 196, 255 - help.glow); + } + break; + } + } + } + } + } + + void render_buttons(void) + { + render_buttons(64, 184, 208); + } + + void render_buttons(int r, int g, int b) + { + if (!key.using_touch) + { + return; + } + + render_buttons(1, false, r, g, b); + } + + void render_buttons(int r, int g, int b, bool textbox_style) + { + touch::textbox_style = textbox_style; + render_buttons(r, g, b); + touch::textbox_style = false; + } + + void render(void) + { + if (!key.using_touch) + { + return; + } + + int scale = get_scale(); + refresh_buttons(); + + render_buttons(scale, true, 64, 184, 208); + } + + void reset(void) + { + for (int i = 0; i < fingers.size(); i++) + { + fingers[i].pressed = false; + fingers[i].on_button = false; + } + } + + void on_menu_create(void) + { + scroll = 0; + } + + void update_buttons(void) + { + if (!use_buttons || graphics.fademode != FADE_NONE) + { + return; + } + + + SDL_Rect stretch_rect; + graphics.get_stretch_info(&stretch_rect); + + SDL_Point point; + SDL_Rect rect; + + for (int buttonId = 0; buttonId < all_buttons.size(); buttonId++) + { + TouchButton* button = all_buttons[buttonId]; + button->down = false; + + for (int fingerId = 0; fingerId < fingers.size(); fingerId++) + { + if (button->ui) + { + point.x = fingers[fingerId].x; + point.y = fingers[fingerId].y; + } + else + { + point.x = (fingers[fingerId].x - stretch_rect.x) * SCREEN_WIDTH_PIXELS / stretch_rect.w; + point.y = (fingers[fingerId].y - stretch_rect.y) * SCREEN_HEIGHT_PIXELS / stretch_rect.h; + } + get_rect(button, &rect); + + if (SDL_PointInRect(&point, &rect) && button->active && !button->disabled) + { + if (fingers[fingerId].pressed) + { + button->pressed = true; + } + + button->down = true; + button->fingerId = fingers[fingerId].id; + fingers[fingerId].on_button = true; + break; + } + } + } + } + + bool button_tapped(TouchButtonID button) + { + if (use_buttons && key.using_touch && buttons[button].active && buttons[button].down && !buttons[button].disabled) + { + for (int i = 0; i < fingers.size(); i++) + { + if (fingers[i].id == buttons[button].fingerId) + { + return fingers[i].pressed; + } + } + } + return false; + } + + bool touching_right(void) + { + int width; + int height; + gameScreen.GetScreenSize(&width, &height); + + for (int i = 0; i < fingers.size(); i++) + { + if (fingers[i].id == swipe_finger) + { + continue; + } + + if (fingers[i].on_button) + { + continue; + } + + if (fingers[i].x > width / 2) + { + return true; + } + } + return false; + } + + bool screen_down(void) + { + for (int i = 0; i < fingers.size(); i++) + { + if (fingers[i].on_button && use_buttons) + { + continue; + } + + if (fingers[i].pressed) + { + // Consume the input, so we don't accidentally start pressing a button or anything + fingers[i].pressed = false; + } + return true; + } + return false; + } + + void update_swipe_finger(void) + { + if (style != TOUCH_STYLE_SWIPE) + { + swipe_delta = 0; + swipe_finger = -1; + return; + } + + int width; + int height; + gameScreen.GetScreenSize(&width, &height); + + VVV_Finger* finger = NULL; + for (int i = 0; i < fingers.size(); i++) + { + if (swipe_finger == -1 && fingers[i].x < width / 2) + { + swipe_finger = fingers[i].id; + swipe_x = fingers[i].x; + swipe_delta = 0; + } + + if (fingers[i].id != swipe_finger) + { + continue; + } + + if (fingers[i].pressed) + { + // Consume the input, so we don't accidentally start pressing a button or anything + fingers[i].pressed = false; + } + finger = &fingers[i]; + break; + } + + if (finger == NULL) + { + swipe_finger = -1; + swipe_delta = 0; + swipe_x = 0; + return; + } + + int delta = finger->x - swipe_x; + if (delta > TOUCH_SWIPE_SENSITIVITY || delta < -TOUCH_SWIPE_SENSITIVITY) + { + swipe_delta = delta; + swipe_x = finger->x; + } + } +} diff --git a/desktop_version/src/Touch.h b/desktop_version/src/Touch.h new file mode 100644 index 0000000000..db6d96d988 --- /dev/null +++ b/desktop_version/src/Touch.h @@ -0,0 +1,146 @@ +#ifndef TOUCH_H +#define TOUCH_H + +#include + +#include +#include + +#define TOUCH_SWIPE_SENSITIVITY 4 + +struct VVV_Finger +{ + float x; + float y; + bool pressed; + bool on_button; + SDL_FingerID id; +}; + +enum TouchControlStyle +{ + TOUCH_STYLE_SWIPE, + TOUCH_STYLE_BUTTONS, + + NUM_TOUCH_STYLES +}; + +enum TouchButtonID +{ + /* General */ + TOUCH_BUTTON_LEFT, + TOUCH_BUTTON_RIGHT, + + /* Gameplay */ + TOUCH_BUTTON_MAP, + + /* Menus */ + TOUCH_BUTTON_CANCEL, + TOUCH_BUTTON_CONFIRM, + + /* Map */ + TOUCH_BUTTON_MAP_MAP, + TOUCH_BUTTON_MAP_CREW, + TOUCH_BUTTON_MAP_STATS, + TOUCH_BUTTON_MAP_QUIT, + + /* Map - Warp to ship */ + TOUCH_BUTTON_MAP_SHIP_WARP, + + /* Map - Quit buttons */ + TOUCH_BUTTON_MAP_QUIT_SAVE, + TOUCH_BUTTON_MAP_QUIT_EXIT, + + /* Map - Return to Secret Lab from Super Gravitron */ + TOUCH_BUTTON_SUPERGRAVITRON_NO, + TOUCH_BUTTON_SUPERGRAVITRON_YES, + + /* Map - Back */ + TOUCH_BUTTON_MAP_BACK, + + /* Quit */ + TOUCH_BUTTON_QUIT_YES, + TOUCH_BUTTON_QUIT_NO, + + NUM_TOUCH_BUTTONS +}; + +enum TouchButtonType +{ + TOUCH_BUTTON_TYPE_NONE, + TOUCH_BUTTON_TYPE_MENU, + TOUCH_BUTTON_TYPE_MENU_LANGUAGE, + TOUCH_BUTTON_TYPE_MENU_TOGGLE, + TOUCH_BUTTON_TYPE_MENU_SLIDER, + TOUCH_BUTTON_TYPE_MAP +}; + +struct TouchButton +{ + int x; + int y; + int width; + int height; + bool down; // Whether the button is currently being pressed + bool pressed; // Whether the button was pressed down and not dragged onto + bool active; // Whether the button is currently active, i.e. visible and usable + bool core; // Whether the button is a "core" button, one which always exists (but not necessarily active) + bool ui; // Whether the button is on the UI layer or not + int id; // The ID for the button, mainly used for menu buttons + bool disabled; // Whether the button is disabled or not (gray and can't use), different from active + bool checked; // If this is a checkbox, whether it's checked or not + int min; // If this is a slider, this is the minimum value + int max; // If this is a slider, this is the maximum value + int* var; // If this is a slider, this is the variable to modify + std::string text; // The text for the button, if it doesn't have an image + Uint8 flags; // Print flags + SDL_Texture* image; // The image that gets displayed on the button, can be NULL + SDL_FingerID fingerId; + TouchButtonType type; +}; + +namespace touch +{ + extern std::vector fingers; + extern TouchButton buttons[NUM_TOUCH_BUTTONS]; + extern std::vector dynamic_buttons; + extern std::vector all_buttons; + extern int scale; + extern bool scroll; + extern TouchControlStyle style; + extern int swipe_delta; + extern SDL_FingerID swipe_finger; + + void refresh_buttons(void); + void update_sliders(); + void reset(void); + void on_menu_create(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_flags(int x, int y, int width, int height, std::string text, int id, Uint8 flags); + void create_menu_button(int x, int y, int width, int height, std::string text, int id, bool disabled); + void create_slider_button(int x, int y, int width, int height, std::string text, int* var, int minvalue, int maxvalue); + void create_toggle_button(int x, int y, int width, int height, std::string text, int id, bool checked); + + void remove_dynamic_buttons(void); + + void on_button_up(TouchButton* button); + + void init(void); + void render_buttons(void); + void render_buttons(int r, int g, int b); + void render_buttons(int r, int g, int b, bool textbox_style); + void render_buttons(int scale, bool ui, int r, int g, int b); + void render(void); + + bool button_tapped(TouchButtonID button); + bool touching_right(void); + bool screen_down(void); + void update_swipe_finger(void); +} + +#endif /* TOUCH_H */ diff --git a/desktop_version/src/main.cpp b/desktop_version/src/main.cpp index 2f884ec751..4e6c86a598 100644 --- a/desktop_version/src/main.cpp +++ b/desktop_version/src/main.cpp @@ -33,6 +33,7 @@ #include "RenderFixed.h" #include "Screen.h" #include "Script.h" +#include "Touch.h" #include "UtilityClass.h" #include "Vlogging.h" @@ -521,6 +522,10 @@ int main(int argc, char *argv[]) { loc::show_translator_menu = true; } + else if (ARG("-emutouch")) + { + SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1"); + } #ifdef _WIN32 else if (ARG("-console")) { @@ -604,6 +609,9 @@ int main(int argc, char *argv[]) /* We already do the button swapping in ButtonGlyphs, disable SDL's swapping */ SDL_SetHintWithPriority(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0", SDL_HINT_OVERRIDE); + /* We want to capture back inputs */ + SDL_SetHintWithPriority(SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1", SDL_HINT_OVERRIDE); + if(!FILESYSTEM_init(argv[0], baseDir, assetsPath, langDir, fontsDir)) { vlog_error("Unable to initialize filesystem!"); @@ -656,6 +664,9 @@ int main(int argc, char *argv[]) // Set up screen graphics.init(); + // Set up touch input before we load settings + touch::init(); + game.init(); game.seed_use_sdl_getticks = seed_use_sdl_getticks; @@ -856,6 +867,11 @@ int main(int argc, char *argv[]) key.isActive = true; + if (SDL_GetNumTouchDevices() > 0) + { + key.using_touch = true; + } + gamestate_funcs = get_gamestate_funcs(game.gamestate, &num_gamestate_funcs); loop_assign_active_funcs(); diff --git a/desktop_version/src/preloader.cpp b/desktop_version/src/preloader.cpp index 4b47ef9963..239c346b4a 100644 --- a/desktop_version/src/preloader.cpp +++ b/desktop_version/src/preloader.cpp @@ -7,6 +7,7 @@ #include "KeyPoll.h" #include "Localization.h" #include "Maths.h" +#include "Touch.h" #include "UtilityClass.h" #include "VFormat.h" @@ -23,7 +24,7 @@ void preloaderinput(void) { game.press_action = false; - if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip)) { + if (key.isDown(KEYBOARD_z) || key.isDown(KEYBOARD_SPACE) || key.isDown(KEYBOARD_v) || key.isDown(game.controllerButton_flip) || touch::screen_down()) { game.press_action = true; } diff --git a/desktop_version/touch/buttons/button_globe.png b/desktop_version/touch/buttons/button_globe.png new file mode 100644 index 0000000000..6d4143dd0e Binary files /dev/null and b/desktop_version/touch/buttons/button_globe.png differ diff --git a/desktop_version/touch/buttons/button_left.png b/desktop_version/touch/buttons/button_left.png new file mode 100644 index 0000000000..a9510ed847 Binary files /dev/null and b/desktop_version/touch/buttons/button_left.png differ diff --git a/desktop_version/touch/buttons/button_map.png b/desktop_version/touch/buttons/button_map.png new file mode 100644 index 0000000000..f5c4266c4b Binary files /dev/null and b/desktop_version/touch/buttons/button_map.png differ diff --git a/desktop_version/touch/buttons/button_quit.png b/desktop_version/touch/buttons/button_quit.png new file mode 100644 index 0000000000..cf944ed238 Binary files /dev/null and b/desktop_version/touch/buttons/button_quit.png differ diff --git a/desktop_version/touch/buttons/button_right.png b/desktop_version/touch/buttons/button_right.png new file mode 100644 index 0000000000..fee938664f Binary files /dev/null and b/desktop_version/touch/buttons/button_right.png differ diff --git a/desktop_version/touch/tutorial/arrowleft.png b/desktop_version/touch/tutorial/arrowleft.png new file mode 100644 index 0000000000..a42d1d3d1f Binary files /dev/null and b/desktop_version/touch/tutorial/arrowleft.png differ diff --git a/desktop_version/touch/tutorial/arrowright.png b/desktop_version/touch/tutorial/arrowright.png new file mode 100644 index 0000000000..b03ea44f90 Binary files /dev/null and b/desktop_version/touch/tutorial/arrowright.png differ diff --git a/desktop_version/touch/tutorial/lefthand_far.png b/desktop_version/touch/tutorial/lefthand_far.png new file mode 100644 index 0000000000..cb7b05f29d Binary files /dev/null and b/desktop_version/touch/tutorial/lefthand_far.png differ diff --git a/desktop_version/touch/tutorial/lefthand_near.png b/desktop_version/touch/tutorial/lefthand_near.png new file mode 100644 index 0000000000..7c941647e3 Binary files /dev/null and b/desktop_version/touch/tutorial/lefthand_near.png differ diff --git a/desktop_version/touch/tutorial/lefthand_off.png b/desktop_version/touch/tutorial/lefthand_off.png new file mode 100644 index 0000000000..a62c4fd215 Binary files /dev/null and b/desktop_version/touch/tutorial/lefthand_off.png differ diff --git a/desktop_version/touch/tutorial/righthand_far.png b/desktop_version/touch/tutorial/righthand_far.png new file mode 100644 index 0000000000..dbc850dc3e Binary files /dev/null and b/desktop_version/touch/tutorial/righthand_far.png differ diff --git a/desktop_version/touch/tutorial/righthand_near.png b/desktop_version/touch/tutorial/righthand_near.png new file mode 100644 index 0000000000..eb661ca36f Binary files /dev/null and b/desktop_version/touch/tutorial/righthand_near.png differ diff --git a/desktop_version/touch/tutorial/righthand_off.png b/desktop_version/touch/tutorial/righthand_off.png new file mode 100644 index 0000000000..d7c51eefc1 Binary files /dev/null and b/desktop_version/touch/tutorial/righthand_off.png differ diff --git a/desktop_version/touch/tutorial/touchscreen.png b/desktop_version/touch/tutorial/touchscreen.png new file mode 100644 index 0000000000..43578e7aae Binary files /dev/null and b/desktop_version/touch/tutorial/touchscreen.png differ