diff --git a/desktop_version/CMakeLists.txt b/desktop_version/CMakeLists.txt index 9e60db8b..e757c483 100644 --- a/desktop_version/CMakeLists.txt +++ b/desktop_version/CMakeLists.txt @@ -104,6 +104,7 @@ set(VVV_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 7eaecbdd..c40652e7 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/src/FileSystemUtils.cpp b/desktop_version/src/FileSystemUtils.cpp index 5643182d..e617cd2c 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 035070cf..8271d723 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" @@ -4905,6 +4906,11 @@ 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, "lang") == 0) { loc::lang = std::string(pText); @@ -5183,6 +5189,8 @@ 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, "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); @@ -6813,6 +6821,7 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) option(loc::gettext("graphics")); option(loc::gettext("audio")); option(loc::gettext("game pad")); + option(loc::gettext("touch input")); option(loc::gettext("accessibility")); option(loc::gettext("language"), !translator_cutscene_test); option(loc::gettext("return")); @@ -6885,6 +6894,13 @@ void Game::createmenu( enum Menu::MenuName t, bool samemenu/*= false*/ ) menuyoff = 0; maxspacing = 10; break; + case Menu::touch_input: + option(loc::gettext("control style"), false); + option(loc::gettext("ui scale")); + option(loc::gettext("return")); + menuyoff = 0; + maxspacing = 15; + break; case Menu::language: if (loc::languagelist.empty()) { diff --git a/desktop_version/src/Game.h b/desktop_version/src/Game.h index 362a956c..9838f85b 100644 --- a/desktop_version/src/Game.h +++ b/desktop_version/src/Game.h @@ -64,6 +64,7 @@ namespace Menu audiooptions, accessibility, controller, + touch_input, language, translator_main, translator_options, diff --git a/desktop_version/src/Graphics.cpp b/desktop_version/src/Graphics.cpp index ed5c93f1..08119dfe 100644 --- a/desktop_version/src/Graphics.cpp +++ b/desktop_version/src/Graphics.cpp @@ -19,6 +19,7 @@ #include "RoomnameTranslator.h" #include "Screen.h" #include "Script.h" +#include "Touch.h" #include "UtilityClass.h" #include "VFormat.h" #include "Vlogging.h" @@ -1174,6 +1175,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}; @@ -3464,6 +3490,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) @@ -3538,6 +3566,8 @@ void Graphics::render(void) get_stretch_info(&rect); copy_texture(gameTexture, NULL, &rect, 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 c37622a2..fe1274c4 100644 --- a/desktop_version/src/Graphics.h +++ b/desktop_version/src/Graphics.h @@ -161,6 +161,8 @@ public: 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 @@ public: 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 571a1f00..49364cdc 100644 --- a/desktop_version/src/GraphicsResources.cpp +++ b/desktop_version/src/GraphicsResources.cpp @@ -429,6 +429,10 @@ 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_sprites_translated = NULL; im_flipsprites_translated = NULL; @@ -476,6 +480,10 @@ void GraphicsResources::destroy(void) CLEAR(im_sprites_translated); CLEAR(im_flipsprites_translated); + + CLEAR(im_button_left); + CLEAR(im_button_right); + CLEAR(im_button_map); #undef CLEAR VVV_freefunc(SDL_FreeSurface, im_sprites_surf); diff --git a/desktop_version/src/GraphicsResources.h b/desktop_version/src/GraphicsResources.h index f7973c68..a2ed89ab 100644 --- a/desktop_version/src/GraphicsResources.h +++ b/desktop_version/src/GraphicsResources.h @@ -48,6 +48,11 @@ public: 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_Surface* LoadImageSurface(const char* filename); diff --git a/desktop_version/src/Input.cpp b/desktop_version/src/Input.cpp index aab9a9a6..476eba11 100644 --- a/desktop_version/src/Input.cpp +++ b/desktop_version/src/Input.cpp @@ -23,6 +23,7 @@ #include "RoomnameTranslator.h" #include "Screen.h" #include "Script.h" +#include "Touch.h" #include "UtilityClass.h" #include "Vlogging.h" @@ -1055,12 +1056,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) { @@ -1975,6 +1982,28 @@ static void menuactionpress(void) break; } break; + case Menu::touch_input: + switch (game.currentmenuoption) + { + case 0: + music.playef(Sound_CRY); + break; + case 1: + touch::scale += 0.5; + music.playef(Sound_VIRIDIAN); + if (touch::scale > 2) + { + touch::scale = 0.5; + } + game.savestatsandsettings_menu(); + break; + case 2: + music.playef(Sound_VIRIDIAN); + game.returnmenu(); + map.nexttowercolour(); + break; + } + break; case Menu::cleardatamenu: switch (game.currentmenuoption) { @@ -2347,26 +2376,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_tapped() : 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)) + { + 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)) { game.jumpheld = true; } @@ -2385,7 +2425,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))) { if (game.currentmenuname == Menu::language && loc::pre_title_lang_menu) { @@ -2562,16 +2602,17 @@ void gameinput(void) game.press_action = false; game.press_interact = false; - if (key.isDown(KEYBOARD_LEFT) || key.isDown(KEYBOARD_a) || key.controllerWantsLeft(false)) + if (key.isDown(KEYBOARD_LEFT) || key.isDown(KEYBOARD_a) || key.controllerWantsLeft(false) || touch::buttons[TOUCH_BUTTON_LEFT].down) { 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) { 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; } @@ -2583,7 +2624,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; } @@ -2600,7 +2641,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_tapped() + ) + { + game.press_action = true; + } } if (game.press_action && !game.jumpheld) @@ -2632,7 +2678,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; } @@ -2977,7 +3024,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; @@ -3099,15 +3146,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; } @@ -3115,8 +3162,8 @@ 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(27) && !game.mapheld) + if (key.isDown(KEYBOARD_ENTER) || key.isDown(game.controllerButton_map) || touch::button_tapped(TOUCH_BUTTON_CONFIRM)) game.press_map = true; + if ((key.isDown(27) || touch::button_tapped(TOUCH_BUTTON_CANCEL)) && !game.mapheld) { game.mapheld = true; if (game.menupage < 9 @@ -3137,7 +3184,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) || touch::button_tapped(TOUCH_BUTTON_CONFIRM)) + { + 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 @@ -3338,11 +3389,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; } @@ -3355,7 +3411,7 @@ 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 (key.isDown(27) || touch::button_tapped(TOUCH_BUTTON_CANCEL)) { if (!map.custommode || map.custommodeforreal) { @@ -3482,7 +3538,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_tapped()) { game.creditposition -= 6; if (game.creditposition <= -Credits::creditmaxposition) @@ -3530,7 +3586,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_tapped()) { game.creditposx++; game.oldcreditposx++; diff --git a/desktop_version/src/KeyPoll.cpp b/desktop_version/src/KeyPoll.cpp index b56b9d47..ab3f93b2 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" @@ -60,6 +61,8 @@ KeyPoll::KeyPoll(void) linealreadyemptykludge = false; isActive = true; + + using_touch = false; } void KeyPoll::enabletextentry(void) @@ -210,6 +213,20 @@ bool cycle_language(bool should_recompute_textboxes) return should_recompute_textboxes; } +static void remove_finger(int i) +{ + for (int j = 0; j < NUM_TOUCH_BUTTONS; j++) + { + if (touch::buttons[j].fingerId == touch::fingers[i].id) + { + touch::buttons[j].down = false; + touch::buttons[j].fingerId = -1; + } + } + + touch::fingers.erase(touch::fingers.begin() + i); +} + void KeyPoll::Poll(void) { static int raw_mousex = 0; @@ -224,6 +241,12 @@ 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(); + while (SDL_PollEvent(&evt)) { switch (evt.type) @@ -439,6 +462,61 @@ 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); + } + } + + 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) @@ -520,6 +598,7 @@ void KeyPoll::Poll(void) switch (evt.type) { case SDL_KEYDOWN: + using_touch = false; if (evt.key.repeat == 0) { hidemouse = true; @@ -528,6 +607,7 @@ void KeyPoll::Poll(void) case SDL_TEXTINPUT: case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERAXISMOTION: + using_touch = false; hidemouse = true; break; case SDL_MOUSEMOTION: @@ -560,6 +640,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 5718ebf0..aaa939f4 100644 --- a/desktop_version/src/KeyPoll.h +++ b/desktop_version/src/KeyPoll.h @@ -72,6 +72,8 @@ public: bool linealreadyemptykludge; + bool using_touch; + private: std::map controllers; std::map buttonmap; diff --git a/desktop_version/src/Render.cpp b/desktop_version/src/Render.cpp index 82132350..f43dc26d 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" @@ -391,10 +392,14 @@ 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); } @@ -763,6 +768,31 @@ static void menurender(void) break; } + case Menu::touch_input: + { + 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); + break; + case 1: + // Display touch buttons! + key.using_touch = true; + + 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]; + vformat_buf(buffer, sizeof(buffer), loc::gettext("Current scale: {scale}.{extra}x"), "scale:int, extra:int", + (int)touch::scale, + (int)((float)((float)touch::scale - (int)touch::scale) * 10) + ); + font::print(PR_CEN, -1, 75, buffer, tr, tg, tb); + break; + } + } + break; case Menu::language: if (loc::languagelist.empty()) { @@ -2432,12 +2462,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)); } @@ -3507,12 +3544,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/Script.cpp b/desktop_version/src/Script.cpp index d97fa9cf..d7cfad2d 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" @@ -802,7 +803,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_tapped()) game.jumpheld = true; } game.backgroundtext = false; @@ -1828,7 +1829,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_tapped()) game.jumpheld = true; } game.backgroundtext = false; } @@ -1851,7 +1852,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_tapped()) game.jumpheld = true; } game.backgroundtext = false; } @@ -1872,7 +1873,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_tapped()) game.jumpheld = true; } game.backgroundtext = false; } diff --git a/desktop_version/src/Touch.cpp b/desktop_version/src/Touch.cpp new file mode 100644 index 00000000..939be707 --- /dev/null +++ b/desktop_version/src/Touch.cpp @@ -0,0 +1,262 @@ +#include "Touch.h" + +#include +#include + +#include "Constants.h" +#include "Graphics.h" +#include "GraphicsResources.h" +#include "KeyPoll.h" +#include "Screen.h" +#include "Script.h" +#include "UtilityClass.h" + +namespace touch +{ + std::vector fingers; + TouchButton buttons[NUM_TOUCH_BUTTONS]; + float scale; + + int get_rect(TouchButton* button, SDL_Rect* rect) + { + rect->x = button->x; + rect->y = button->y; + 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) * scale); + } + + void init(void) + { + scale = 1; + + for (int i = 0; i < NUM_TOUCH_BUTTONS; i++) + { + buttons[i].image = NULL; + buttons[i].active = false; + buttons[i].down = false; + buttons[i].fingerId = -1; + } + } + + 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; + + // First, reset all buttons + for (int i = 0; i < NUM_TOUCH_BUTTONS; i++) + { + buttons[i].active = false; + } + + // Now, set the buttons that are active + + switch (game.gamestate) + { + case GAMEMODE: + if (!script.running && game.hascontrol) + { + buttons[TOUCH_BUTTON_LEFT].active = true; + buttons[TOUCH_BUTTON_RIGHT].active = true; + buttons[TOUCH_BUTTON_MAP].active = true; + } + break; + + case TITLEMODE: + if (game.menustart) + { + buttons[TOUCH_BUTTON_LEFT].active = true; + buttons[TOUCH_BUTTON_RIGHT].active = true; + buttons[TOUCH_BUTTON_CANCEL].active = true; + buttons[TOUCH_BUTTON_CONFIRM].active = true; + } + break; + case TELEPORTERMODE: + if (game.useteleporter) + { + buttons[TOUCH_BUTTON_LEFT].active = true; + buttons[TOUCH_BUTTON_RIGHT].active = true; + buttons[TOUCH_BUTTON_CANCEL].active = true; + buttons[TOUCH_BUTTON_CONFIRM].active = true; + } + break; + case MAPMODE: + buttons[TOUCH_BUTTON_LEFT].active = true; + buttons[TOUCH_BUTTON_RIGHT].active = true; + buttons[TOUCH_BUTTON_CANCEL].active = true; + buttons[TOUCH_BUTTON_CONFIRM].active = true; + break; + case GAMECOMPLETE: + case GAMECOMPLETE2: + case EDITORMODE: + case PRELOADER: + default: + break; + } + } + + void render(void) + { + if (!key.using_touch) + { + return; + } + + int scale = get_scale(); + refresh_buttons(); + + for (int i = 0; i < NUM_TOUCH_BUTTONS; i++) + { + SDL_Rect rect; + get_rect(&buttons[i], &rect); + + if (buttons[i].image != NULL && buttons[i].active) + { + graphics.draw_texture(buttons[i].image, rect.x, rect.y + (buttons[i].down ? 2 * scale : 0), scale, scale); + } + } + } + + void reset(void) + { + for (int i = 0; i < NUM_TOUCH_BUTTONS; i++) + { + buttons[i].down = false; + buttons[i].fingerId = -1; + } + + for (int i = 0; i < fingers.size(); i++) + { + fingers[i].pressed = false; + fingers[i].on_button = false; + } + } + + void update_buttons(void) + { + if (graphics.fademode != FADE_NONE) + { + return; + } + + SDL_Point point; + SDL_Rect rect; + + for (int buttonId = 0; buttonId < NUM_TOUCH_BUTTONS; buttonId++) + { + TouchButton* button = &buttons[buttonId]; + button->down = false; + + for (int fingerId = 0; fingerId < fingers.size(); fingerId++) + { + point.x = fingers[fingerId].x; + point.y = fingers[fingerId].y; + get_rect(button, &rect); + + if (SDL_PointInRect(&point, &rect) && button->active) + { + button->down = true; + button->fingerId = fingers[fingerId].id; + fingers[fingerId].on_button = true; + break; + } + } + } + } + + bool button_tapped(TouchButtonID button) + { + if (key.using_touch && buttons[button].active && buttons[button].down) + { + 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].on_button) + { + continue; + } + + if (fingers[i].x > width / 2) + { + return true; + } + } + return false; + } + + bool screen_tapped(void) + { + for (int i = 0; i < fingers.size(); i++) + { + if (fingers[i].on_button) + { + continue; + } + + return fingers[i].pressed; + } + return false; + } +} diff --git a/desktop_version/src/Touch.h b/desktop_version/src/Touch.h new file mode 100644 index 00000000..ea1bb427 --- /dev/null +++ b/desktop_version/src/Touch.h @@ -0,0 +1,63 @@ +#ifndef TOUCH_H +#define TOUCH_H + +#include + +#include + +struct VVV_Finger +{ + float x; + float y; + bool pressed; + bool on_button; + SDL_FingerID id; +}; + +enum TouchButtonID +{ + /* General */ + TOUCH_BUTTON_LEFT, + TOUCH_BUTTON_RIGHT, + + /* Gameplay */ + TOUCH_BUTTON_MAP, + + /* Menus */ + TOUCH_BUTTON_CANCEL, + TOUCH_BUTTON_CONFIRM, + + NUM_TOUCH_BUTTONS +}; + +struct TouchButton +{ + int x; + int y; + int width; + int height; + bool down; + bool active; + SDL_Texture* image; + SDL_FingerID fingerId; +}; + +namespace touch +{ + extern std::vector fingers; + extern TouchButton buttons[NUM_TOUCH_BUTTONS]; + extern float scale; + + void refresh_buttons(void); + void reset(void); + void update_buttons(void); + + void init(void); + void render(void); + + bool button_tapped(TouchButtonID button); + bool touching_right(void); + bool screen_tapped(void); +} + +#endif /* TOUCH_H */ diff --git a/desktop_version/src/main.cpp b/desktop_version/src/main.cpp index 1dc9f989..6da8aea4 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" @@ -527,6 +528,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")) { @@ -614,6 +619,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; @@ -813,6 +821,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 4b47ef99..0e42cb94 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_tapped()) { game.press_action = true; } diff --git a/desktop_version/touch/buttons/button_left.png b/desktop_version/touch/buttons/button_left.png new file mode 100644 index 00000000..a9510ed8 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 00000000..f5c4266c Binary files /dev/null and b/desktop_version/touch/buttons/button_map.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 00000000..fee93866 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 00000000..a42d1d3d 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 00000000..b03ea44f 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 00000000..cb7b05f2 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 00000000..7c941647 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 00000000..a62c4fd2 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 00000000..dbc850dc 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 00000000..eb661ca3 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 00000000..d7c51eef 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 00000000..43578e7a Binary files /dev/null and b/desktop_version/touch/tutorial/touchscreen.png differ