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