diff --git a/LICENSE.txt b/LICENSE.txt index 503dd62d2..772f86728 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -102,6 +102,9 @@ grorp: using the font "undefined medium" (https://undefined-medium.com/), which is licensed under the SIL Open Font License, Version 1.1 modified by DS + textures/base/pack/dig_btn.png + textures/base/pack/place_btn.png + derived by editing the text in aux1_btn.svg License of Luanti source code ------------------------------- diff --git a/android/icons/dig_btn.svg b/android/icons/dig_btn.svg new file mode 100644 index 000000000..cbfa69e6f --- /dev/null +++ b/android/icons/dig_btn.svg @@ -0,0 +1,148 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + LMB + + + diff --git a/android/icons/place_btn.svg b/android/icons/place_btn.svg new file mode 100644 index 000000000..b32dfc018 --- /dev/null +++ b/android/icons/place_btn.svg @@ -0,0 +1,148 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + RMB + + + diff --git a/builtin/common/settings/dlg_settings.lua b/builtin/common/settings/dlg_settings.lua index 570c01cd5..6f0b772af 100644 --- a/builtin/common/settings/dlg_settings.lua +++ b/builtin/common/settings/dlg_settings.lua @@ -259,6 +259,17 @@ local function load() ["true"] = fgettext_ne("Enabled"), ["false"] = fgettext_ne("Disabled"), } + + get_setting_info("touch_interaction_style").option_labels = { + ["tap"] = fgettext_ne("Tap"), + ["tap_crosshair"] = fgettext_ne("Tap with crosshair"), + ["buttons_crosshair"] = fgettext("Buttons with crosshair"), + } + + get_setting_info("touch_punch_gesture").option_labels = { + ["short_tap"] = fgettext_ne("Short tap"), + ["long_tap"] = fgettext_ne("Long tap"), + } end @@ -359,6 +370,7 @@ local function check_requirements(name, requires) local video_driver = core.get_active_driver() local touch_support = core.irrlicht_device_supports_touch() local touch_controls = core.settings:get("touch_controls") + local touch_interaction_style = core.settings:get("touch_interaction_style") local special = { android = PLATFORM == "Android", desktop = PLATFORM ~= "Android", @@ -369,6 +381,7 @@ local function check_requirements(name, requires) keyboard_mouse = not touch_support or (touch_controls == "auto" or not core.is_yes(touch_controls)), opengl = (video_driver == "opengl" or video_driver == "opengl3"), gles = video_driver:sub(1, 5) == "ogles", + touch_interaction_style_tap = touch_interaction_style ~= "buttons_crosshair", } for req_key, req_value in pairs(requires) do diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 5d3462e41..3ae55aecd 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -167,6 +167,36 @@ invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false # Requires: touch_support touch_controls (Touchscreen controls) enum auto auto,true,false +# The kind of digging/placing controls used. +# +# * Tap +# Long/short tap anywhere on the screen to interact. +# Interaction happens at finger position. +# +# * Tap with crosshair +# Long/short tap anywhere on the screen to interact. +# Interaction happens at crosshair position. +# +# * Buttons with crosshair +# Use dedicated dig/place buttons to interact. +# Interaction happens at crosshair position. +# +# Requires: touchscreen +touch_interaction_style (Interaction style) enum tap tap,tap_crosshair,buttons_crosshair + +# The gesture for punching players/entities. +# This can be overridden by games and mods. +# +# * Short tap +# Easy to use and well-known from other games that shall not be named. +# +# * Long tap +# Known from the classic Luanti mobile controls. +# Combat is more or less impossible. +# +# Requires: touchscreen, touch_interaction_style_tap +touch_punch_gesture (Punch gesture) enum short_tap short_tap,long_tap + # Touchscreen sensitivity multiplier. # # Requires: touchscreen @@ -182,12 +212,6 @@ touchscreen_threshold (Movement threshold) int 20 0 100 # Requires: touchscreen touch_long_tap_delay (Threshold for long taps) int 400 100 1000 -# Use crosshair to select object instead of whole screen. -# If enabled, a crosshair will be shown and will be used for selecting object. -# -# Requires: touchscreen -touch_use_crosshair (Use crosshair for touch screen) bool false - # Fixes the position of virtual joystick. # If disabled, virtual joystick will center to first-touch's position. # @@ -200,20 +224,6 @@ fixed_virtual_joystick (Fixed virtual joystick) bool false # Requires: touchscreen virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false -# The gesture for punching players/entities. -# This can be overridden by games and mods. -# -# * short_tap -# Easy to use and well-known from other games that shall not be named. -# -# * long_tap -# Known from the classic Luanti mobile controls. -# Combat is more or less impossible. -# -# Requires: touchscreen -touch_punch_gesture (Punch gesture) enum short_tap short_tap,long_tap - - [Graphics and Audio] [*Graphics] diff --git a/doc/texture_packs.md b/doc/texture_packs.md index 256915b81..d386006f2 100644 --- a/doc/texture_packs.md +++ b/doc/texture_packs.md @@ -139,21 +139,34 @@ are placeholders intended to be overwritten by the game. ### Android textures -* `drop_btn.png` -* `fast_btn.png` -* `fly_btn.png` -* `jump_btn.png` -* `noclip_btn.png` +* `dig_btn.png` +* `place_btn.png` + +* `jump_btn.png` +* `down.png` +* `zoom.png` +* `aux1_btn.png` +* `overflow_btn.png` -* `camera_btn.png` * `chat_btn.png` * `inventory_btn.png` -* `rangeview_btn.png` - -* `debug_btn.png` -* `overflow_btn.png` +* `drop_btn.png` * `exit_btn.png` +* `fly_btn.png` +* `fast_btn.png` +* `noclip_btn.png` +* `debug_btn.png` +* `camera_btn.png` +* `rangeview_btn.png` +* `minimap_btn.png` +* `chat_hide_btn.png` +* `chat_show_btn.png` + +* `joystick_off.png` +* `joystick_bg.png` +* `joystick_center.png` + Texture Overrides ----------------- diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 8b0eaa677..205fe5934 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -549,9 +549,9 @@ void set_default_settings() settings->setDefault("touchscreen_sensitivity", "0.2"); settings->setDefault("touchscreen_threshold", "20"); settings->setDefault("touch_long_tap_delay", "400"); - settings->setDefault("touch_use_crosshair", "false"); settings->setDefault("fixed_virtual_joystick", "false"); settings->setDefault("virtual_joystick_triggers_aux1", "false"); + settings->setDefault("touch_interaction_style", "tap"); settings->setDefault("touch_punch_gesture", "short_tap"); settings->setDefault("clickable_chat_weblinks", "true"); diff --git a/src/gui/touchcontrols.cpp b/src/gui/touchcontrols.cpp index d3c072d6f..4cea023ab 100644 --- a/src/gui/touchcontrols.cpp +++ b/src/gui/touchcontrols.cpp @@ -15,6 +15,7 @@ #include "client/guiscalingfilter.h" #include "client/renderingengine.h" #include "client/texturesource.h" +#include "util/enum_string.h" #include "util/numeric.h" #include "irr_gui_ptr.h" #include "IGUIImage.h" @@ -78,15 +79,18 @@ bool TouchControls::buttonsHandlePress(std::vector &buttons, size_t for (button_info &btn : buttons) { if (btn.gui_button.get() == element) { + // Allow moving the camera with the same finger that holds dig/place. + bool absorb = btn.id != dig_id && btn.id != place_id; + assert(std::find(btn.pointer_ids.begin(), btn.pointer_ids.end(), pointer_id) == btn.pointer_ids.end()); btn.pointer_ids.push_back(pointer_id); if (btn.pointer_ids.size() > 1) - return true; + return absorb; buttonEmitAction(btn, true); btn.repeat_counter = -BUTTON_REPEAT_DELAY; - return true; + return absorb; } } @@ -99,13 +103,16 @@ bool TouchControls::buttonsHandleRelease(std::vector &buttons, size for (button_info &btn : buttons) { auto it = std::find(btn.pointer_ids.begin(), btn.pointer_ids.end(), pointer_id); if (it != btn.pointer_ids.end()) { + // Don't absorb since we didn't absorb the press event either. + bool absorb = btn.id != dig_id && btn.id != place_id; + btn.pointer_ids.erase(it); if (!btn.pointer_ids.empty()) - return true; + return absorb; buttonEmitAction(btn, false); - return true; + return absorb; } } @@ -117,6 +124,8 @@ bool TouchControls::buttonsStep(std::vector &buttons, float dtime) bool has_pointers = false; for (button_info &btn : buttons) { + if (btn.id == dig_id || btn.id == place_id) + continue; // key repeats would cause glitches here if (btn.pointer_ids.empty()) continue; has_pointers = true; @@ -205,7 +214,7 @@ static const KeyPress &id_to_keypress(touch_gui_button_id id) static const char *setting_names[] = { - "touch_use_crosshair", + "touch_interaction_style", "touchscreen_threshold", "touch_long_tap_delay", "fixed_virtual_joystick", "virtual_joystick_triggers_aux1", "touch_layout", @@ -232,14 +241,20 @@ void TouchControls::settingChangedCallback(const std::string &name, void *data) void TouchControls::readSettings() { - m_use_crosshair = g_settings->getBool("touch_use_crosshair"); + const std::string &s = g_settings->get("touch_interaction_style"); + if (!string_to_enum(es_TouchInteractionStyle, m_interaction_style, s)) { + m_interaction_style = TAP; + warningstream << "Invalid touch_interaction_style value" << std::endl; + } + m_touchscreen_threshold = g_settings->getU16("touchscreen_threshold"); m_long_tap_delay = g_settings->getU16("touch_long_tap_delay"); m_fixed_joystick = g_settings->getBool("fixed_virtual_joystick"); m_joystick_triggers_aux1 = g_settings->getBool("virtual_joystick_triggers_aux1"); - // Note that "fixed_virtual_joystick" and "virtual_joystick_triggers_aux1" - // also affect the layout. + // Note that other settings also affect the layout: + // - ButtonLayout::loadFromSettings: "touch_interaction_style" and "virtual_joystick_triggers_aux1" + // - applyLayout: "fixed_virtual_joystick" applyLayout(ButtonLayout::loadFromSettings()); } @@ -305,8 +320,8 @@ void TouchControls::applyLayout(const ButtonLayout &layout) overflow_buttons.erase(std::remove_if( overflow_buttons.begin(), overflow_buttons.end(), [&](touch_gui_button_id id) { - // There's no sense in adding the overflow button to the overflow - // menu (also, it's impossible since it doesn't have a keycode). + // There would be no sense in adding the overflow button to the + // overflow menu. return !mayAddButton(id) || id == overflow_id; }), overflow_buttons.end()); @@ -346,13 +361,10 @@ TouchControls::~TouchControls() bool TouchControls::mayAddButton(touch_gui_button_id id) { - if (!ButtonLayout::isButtonAllowed(id)) - return false; - if (id == aux1_id && m_joystick_triggers_aux1) - return false; - if (id != overflow_id && !id_to_keypress(id)) - return false; - return true; + assert(ButtonLayout::isButtonValid(id)); + assert(ButtonLayout::isButtonAllowed(id)); + // The overflow button doesn't need a keycode to be valid. + return id == overflow_id || id_to_keypress(id); } void TouchControls::addButton(std::vector &buttons, touch_gui_button_id id, @@ -363,6 +375,7 @@ void TouchControls::addButton(std::vector &buttons, touch_gui_butto loadButtonTexture(btn_gui_button, image); button_info &btn = buttons.emplace_back(); + btn.id = id; btn.keypress = id_to_keypress(id); btn.gui_button = grab_gui_element(btn_gui_button); } @@ -429,7 +442,8 @@ void TouchControls::handleReleaseEvent(size_t pointer_id) // If m_tap_state is already set to TapState::ShortTap, we must keep // that value. Otherwise, many short taps will be ignored if you tap // very fast. - if (!m_move_has_really_moved && !m_move_prevent_short_tap && + if (m_interaction_style != BUTTONS_CROSSHAIR && + !m_move_has_really_moved && !m_move_prevent_short_tap && m_tap_state != TapState::LongTap) { m_tap_state = TapState::ShortTap; } else { @@ -655,7 +669,9 @@ void TouchControls::step(float dtime) applyJoystickStatus(); // if a new placed pointer isn't moved for some time start digging - if (m_has_move_id && !m_move_has_really_moved && m_tap_state == TapState::None) { + if (m_interaction_style != BUTTONS_CROSSHAIR && + m_has_move_id && !m_move_has_really_moved && + m_tap_state == TapState::None) { u64 delta = porting::getDeltaMs(m_move_downtime, porting::getTimeMs()); if (delta > m_long_tap_delay) { @@ -669,7 +685,7 @@ void TouchControls::step(float dtime) // shootline when a touch event occurs. // Only updating when m_has_move_id means that the shootline will stay at // it's last in-world position when the player doesn't need it. - if (!m_use_crosshair && (m_has_move_id || m_had_move_id)) { + if (m_interaction_style == TAP && (m_has_move_id || m_had_move_id)) { m_shootline = m_device ->getSceneManager() ->getSceneCollisionManager() @@ -757,6 +773,9 @@ void TouchControls::show() void TouchControls::applyContextControls(const TouchInteractionMode &mode) { + if (m_interaction_style == BUTTONS_CROSSHAIR) + return; + // Since the pointed thing has already been determined when this function // is called, we cannot use this function to update the shootline. diff --git a/src/gui/touchcontrols.h b/src/gui/touchcontrols.h index b64457ca5..c2ed8ae7b 100644 --- a/src/gui/touchcontrols.h +++ b/src/gui/touchcontrols.h @@ -57,6 +57,7 @@ enum class TapState struct button_info { + touch_gui_button_id id; float repeat_counter; KeyPress keypress; std::vector pointer_ids; @@ -94,7 +95,7 @@ public: return res; } - bool isShootlineAvailable() { return !m_use_crosshair; } + bool isShootlineAvailable() { return m_interaction_style == TAP; } /** * Returns a line which describes what the player is pointing at. @@ -137,7 +138,7 @@ private: s32 m_button_size; // cached settings - bool m_use_crosshair; + TouchInteractionStyle m_interaction_style; double m_touchscreen_threshold; u16 m_long_tap_delay; bool m_fixed_joystick; @@ -161,7 +162,7 @@ private: * The line ends on the camera's far plane. * The coordinates do not contain the camera offset. * - * Only valid if !m_use_crosshair + * Only used for m_interaction_style == TAP */ line3d m_shootline; @@ -243,6 +244,8 @@ private: // map to store the IDs and positions of currently pressed pointers std::unordered_map m_pointer_pos; + // The following are not used if m_interaction_style == BUTTONS_CROSSHAIR + TouchInteractionMode m_last_mode = TouchInteractionMode_END; TapState m_tap_state = TapState::None; diff --git a/src/gui/touchscreeneditor.cpp b/src/gui/touchscreeneditor.cpp index 4a8468ee5..76403973f 100644 --- a/src/gui/touchscreeneditor.cpp +++ b/src/gui/touchscreeneditor.cpp @@ -371,7 +371,7 @@ bool GUITouchscreenLayout::OnEvent(const SEvent& event) } if (event.GUIEvent.Caller == m_gui_reset_btn.get()) { - m_layout = ButtonLayout::predefined; + m_layout = ButtonLayout::loadDefault(); regenerateGui(screensize); return true; } diff --git a/src/gui/touchscreenlayout.cpp b/src/gui/touchscreenlayout.cpp index e0ad5b723..bd97526ff 100644 --- a/src/gui/touchscreenlayout.cpp +++ b/src/gui/touchscreenlayout.cpp @@ -12,6 +12,15 @@ #include "IGUIFont.h" #include "IGUIStaticText.h" +#include "util/enum_string.h" + +const struct EnumString es_TouchInteractionStyle[] = +{ + {TAP, "tap"}, + {TAP_CROSSHAIR, "tap_crosshair"}, + {BUTTONS_CROSSHAIR, "buttons_crosshair"}, + {0, NULL}, +}; const char *button_names[] = { "dig", @@ -73,8 +82,8 @@ const char *button_titles[] = { }; const char *button_image_names[] = { - "", - "", + "dig_btn.png", + "place_btn.png", "jump_btn.png", "down.png", @@ -130,11 +139,21 @@ void ButtonMeta::setPos(v2s32 pos, v2u32 screensize, s32 button_size) offset.Y = (pos.Y - (position.Y * screensize.Y)) / button_size; } +bool ButtonLayout::isButtonValid(touch_gui_button_id id) +{ + return id != joystick_off_id && id != joystick_bg_id && id != joystick_center_id && + id < touch_gui_button_id_END; +} + +static const char *buttons_crosshair = enum_to_string(es_TouchInteractionStyle, BUTTONS_CROSSHAIR); + bool ButtonLayout::isButtonAllowed(touch_gui_button_id id) { - return id != dig_id && id != place_id && - id != joystick_off_id && id != joystick_bg_id && id != joystick_center_id && - id != touch_gui_button_id_END; + if (id == dig_id || id == place_id) + return g_settings->get("touch_interaction_style") == buttons_crosshair; + if (id == aux1_id) + return !g_settings->getBool("virtual_joystick_triggers_aux1"); + return true; } bool ButtonLayout::isButtonRequired(touch_gui_button_id id) @@ -149,7 +168,15 @@ s32 ButtonLayout::getButtonSize(v2u32 screensize) g_settings->getFloat("hud_scaling")); } -const ButtonLayout ButtonLayout::predefined {{ +const ButtonLayout::ButtonMap ButtonLayout::default_data { + {dig_id, { + v2f(1.0f, 1.0f), + v2f(-2.0f, -2.75f), + }}, + {place_id, { + v2f(1.0f, 1.0f), + v2f(-2.0f, -4.25f), + }}, {jump_id, { v2f(1.0f, 1.0f), v2f(-1.0f, -0.5f), @@ -170,28 +197,40 @@ const ButtonLayout ButtonLayout::predefined {{ v2f(1.0f, 1.0f), v2f(-0.75f, -5.0f), }}, -}}; +}; + +ButtonLayout ButtonLayout::postProcessLoaded(const ButtonMap &data) +{ + ButtonLayout layout; + for (const auto &[id, meta] : data) { + assert(isButtonValid(id)); + if (isButtonAllowed(id)) + layout.layout.emplace(id, meta); + else + layout.preserved_disallowed.emplace(id, meta); + } + return layout; +} + +ButtonLayout ButtonLayout::loadDefault() +{ + return postProcessLoaded(default_data); +} ButtonLayout ButtonLayout::loadFromSettings() { - bool restored = false; - ButtonLayout layout; - std::string str = g_settings->get("touch_layout"); if (!str.empty()) { std::istringstream iss(str); try { - layout.deserializeJson(iss); - restored = true; + ButtonMap data = deserializeJson(iss); + return postProcessLoaded(data); } catch (const Json::Exception &e) { warningstream << "Could not parse touchscreen layout: " << e.what() << std::endl; } } - if (!restored) - return predefined; - - return layout; + return loadDefault(); } std::unordered_map> ButtonLayout::texture_cache; @@ -234,7 +273,7 @@ std::vector ButtonLayout::getMissingButtons() std::vector missing_buttons; for (u8 i = 0; i < touch_gui_button_id_END; i++) { touch_gui_button_id btn = (touch_gui_button_id)i; - if (isButtonAllowed(btn) && layout.count(btn) == 0) + if (isButtonValid(btn) && isButtonAllowed(btn) && layout.count(btn) == 0) missing_buttons.push_back(btn); } return missing_buttons; @@ -243,16 +282,19 @@ std::vector ButtonLayout::getMissingButtons() void ButtonLayout::serializeJson(std::ostream &os) const { Json::Value root = Json::objectValue; + root["version"] = 1; root["layout"] = Json::objectValue; - for (const auto &[id, meta] : layout) { - Json::Value button = Json::objectValue; - button["position_x"] = meta.position.X; - button["position_y"] = meta.position.Y; - button["offset_x"] = meta.offset.X; - button["offset_y"] = meta.offset.Y; + for (const auto &list : {layout, preserved_disallowed}) { + for (const auto &[id, meta] : list) { + Json::Value button = Json::objectValue; + button["position_x"] = meta.position.X; + button["position_y"] = meta.position.Y; + button["offset_x"] = meta.offset.X; + button["offset_y"] = meta.offset.Y; - root["layout"][button_names[id]] = button; + root["layout"][button_names[id]] = button; + } } fastWriteJson(root, os); @@ -267,13 +309,19 @@ static touch_gui_button_id button_name_to_id(const std::string &name) return touch_gui_button_id_END; } -void ButtonLayout::deserializeJson(std::istream &is) +ButtonLayout::ButtonMap ButtonLayout::deserializeJson(std::istream &is) { - layout.clear(); + ButtonMap data; Json::Value root; is >> root; + u8 version; + if (root["version"].isUInt()) + version = root["version"].asUInt(); + else + version = 0; + if (!root["layout"].isObject()) throw Json::RuntimeError("invalid type for layout"); @@ -281,7 +329,7 @@ void ButtonLayout::deserializeJson(std::istream &is) Json::ValueIterator iter; for (iter = obj.begin(); iter != obj.end(); iter++) { touch_gui_button_id id = button_name_to_id(iter.name()); - if (!isButtonAllowed(id)) + if (!isButtonValid(id)) throw Json::RuntimeError("invalid button name"); Json::Value &value = *iter; @@ -300,8 +348,20 @@ void ButtonLayout::deserializeJson(std::istream &is) meta.offset.X = value["offset_x"].asFloat(); meta.offset.Y = value["offset_y"].asFloat(); - layout.emplace(id, meta); + data.emplace(id, meta); } + + if (version < 1) { + // Version 0 did not have dig/place buttons, so add them in. + // Otherwise, the missing buttons would cause confusion if the user + // switches to "touch_interaction_style = buttons_crosshair". + // This may result in overlapping buttons (could be fixed by resolving + // collisions in postProcessLoaded). + data.emplace(dig_id, default_data.at(dig_id)); + data.emplace(place_id, default_data.at(place_id)); + } + + return data; } void layout_button_grid(v2u32 screensize, ISimpleTextureSource *tsrc, diff --git a/src/gui/touchscreenlayout.h b/src/gui/touchscreenlayout.h index fe0d99b9d..7a181d701 100644 --- a/src/gui/touchscreenlayout.h +++ b/src/gui/touchscreenlayout.h @@ -7,6 +7,7 @@ #include "irr_ptr.h" #include "irrlichttypes_bloated.h" #include "rect.h" +#include "util/enum_string.h" #include #include @@ -20,9 +21,16 @@ namespace irr::video class ITexture; } +enum TouchInteractionStyle : u8 +{ + TAP, + TAP_CROSSHAIR, + BUTTONS_CROSSHAIR, +}; +extern const struct EnumString es_TouchInteractionStyle[]; + enum touch_gui_button_id : u8 { - // these two are no actual buttons ... yet dig_id = 0, place_id, @@ -75,15 +83,34 @@ struct ButtonMeta { }; struct ButtonLayout { + using ButtonMap = std::unordered_map; + + static bool isButtonValid(touch_gui_button_id id); static bool isButtonAllowed(touch_gui_button_id id); static bool isButtonRequired(touch_gui_button_id id); static s32 getButtonSize(v2u32 screensize); + + // Returns the default layout. + // Note: Indirectly depends on settings. + static ButtonLayout loadDefault(); + // Reads the layout from the "touch_layout" setting. Falls back to loadDefault + // if the setting is absent or invalid. + // Note: Indirectly depends on additional settings. static ButtonLayout loadFromSettings(); static video::ITexture *getTexture(touch_gui_button_id btn, ISimpleTextureSource *tsrc); static void clearTextureCache(); - std::unordered_map layout; + ButtonMap layout; + + // Contains disallowed buttons that have been loaded. + // These are only preserved to be saved again later. + // This exists to prevent data loss: If you edit the layout while some button + // is temporarily disallowed, this prevents that button's custom position + // from being lost. See isButtonAllowed. + // This may result in overlapping buttons when the buttons are allowed again + // (could be fixed by resolving collisions in postProcessLoaded). + ButtonMap preserved_disallowed; core::recti getRect(touch_gui_button_id btn, v2u32 screensize, s32 button_size, ISimpleTextureSource *tsrc); @@ -91,11 +118,12 @@ struct ButtonLayout { std::vector getMissingButtons(); void serializeJson(std::ostream &os) const; - void deserializeJson(std::istream &is); - - static const ButtonLayout predefined; private: + static const ButtonMap default_data; + static ButtonMap deserializeJson(std::istream &is); + static ButtonLayout postProcessLoaded(const ButtonMap &map); + static std::unordered_map> texture_cache; }; diff --git a/src/migratesettings.h b/src/migratesettings.h index 5f6396914..4839f9479 100644 --- a/src/migratesettings.h +++ b/src/migratesettings.h @@ -28,4 +28,11 @@ void migrate_settings() } g_settings->remove("disable_anticheat"); } + + // Convert touch_use_crosshair to touch_interaction_style + if (g_settings->existsLocal("touch_use_crosshair")) { + bool value = g_settings->getBool("touch_use_crosshair"); + g_settings->set("touch_interaction_style", value ? "tap_crosshair" : "tap"); + g_settings->remove("touch_use_crosshair"); + } } diff --git a/textures/base/pack/dig_btn.png b/textures/base/pack/dig_btn.png new file mode 100644 index 000000000..d612d1134 Binary files /dev/null and b/textures/base/pack/dig_btn.png differ diff --git a/textures/base/pack/place_btn.png b/textures/base/pack/place_btn.png new file mode 100644 index 000000000..f834e62c4 Binary files /dev/null and b/textures/base/pack/place_btn.png differ