Add "Touch Targeting" option

This commit is contained in:
Luke Street
2026-06-27 21:20:14 -06:00
parent ebf6f31719
commit f81d25b425
6 changed files with 141 additions and 27 deletions
+1 -1
+13
View File
@@ -46,6 +46,12 @@ enum class FrameInterpMode : u8 {
Unlimited = 2,
};
enum class TouchTargeting : u8 {
Hybrid = 0,
Hold = 1,
Switch = 2,
};
enum class MenuScaling : u8 {
GameCube = 0,
Wii = 1,
@@ -97,6 +103,12 @@ struct ConfigEnumRange<FrameInterpMode> {
static constexpr auto max = FrameInterpMode::Unlimited;
};
template <>
struct ConfigEnumRange<TouchTargeting> {
static constexpr auto min = TouchTargeting::Hybrid;
static constexpr auto max = TouchTargeting::Switch;
};
template <>
struct ConfigEnumRange<MenuScaling> {
static constexpr auto min = MenuScaling::GameCube;
@@ -216,6 +228,7 @@ struct UserSettings {
ConfigVar<bool> invertMouseY;
ConfigVar<bool> freeCamera;
ConfigVar<bool> enableTouchControls;
ConfigVar<TouchTargeting> touchTargeting;
ConfigVar<bool> enableMenuPointer;
ConfigVar<ui::ControlLayout> touchControlsLayout;
ConfigVar<bool> invertCameraXAxis;
+1
View File
@@ -378,6 +378,7 @@ nlohmann::json ConfigImpl<ui::ControlLayout>::dumpToJson(const ConfigVar<ui::Con
}
template class ConfigImpl<dusk::FrameInterpMode>;
template class ConfigImpl<dusk::TouchTargeting>;
template class ConfigImpl<dusk::MenuScaling>;
template class ConfigImpl<dusk::Resampler>;
template class ConfigImpl<dusk::MagicArmorMode>;
+2
View File
@@ -96,6 +96,7 @@ UserSettings g_userSettings = {
.invertMouseY {"game.invertMouseY", false},
.freeCamera {"game.freeCamera", false},
.enableTouchControls {"game.enableTouchControls", false},
.touchTargeting {"game.touchTargeting", TouchTargeting::Hybrid},
.enableMenuPointer {"game.enableMenuPointer", true},
.touchControlsLayout {"game.touchControlsLayout", ui::ControlLayout{}},
.invertCameraXAxis {"game.invertCameraXAxis", false},
@@ -329,6 +330,7 @@ void registerSettings() {
Register(g_userSettings.game.invertMouseY);
Register(g_userSettings.game.freeCamera);
Register(g_userSettings.game.enableTouchControls);
Register(g_userSettings.game.touchTargeting);
Register(g_userSettings.game.enableMenuPointer);
Register(g_userSettings.game.touchControlsLayout);
Register(g_userSettings.game.debugFlyCam);
+59
View File
@@ -77,6 +77,18 @@ constexpr std::array kInterpolationModes = {
"Unlimited",
};
constexpr std::array kTouchTargetingLabels = {
"Hybrid",
"Hold",
"Switch",
};
constexpr std::array kTouchTargetingDescriptions = {
"Tap once to lock on when a target is found. Double-tap when none is found to hold L.",
"L stays held only while your finger is on the button.",
"Tap L to keep it held. Tap again to release it.",
};
constexpr std::array kGyroInputModeLabels = {
"Sensor",
"Mouse",
@@ -407,6 +419,14 @@ bool gyro_enabled() {
return getSettings().game.enableGyroAim || getSettings().game.enableGyroRollgoal;
}
Rml::String touch_targeting_label(TouchTargeting targeting) {
const auto index = static_cast<std::size_t>(targeting);
if (index >= kTouchTargetingLabels.size()) {
return "Unknown";
}
return kTouchTargetingLabels[index];
}
struct ConfigBoolProps {
Rml::String key;
Rml::String icon;
@@ -1003,6 +1023,45 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
pane.clear();
pane.add_text("Open the touch controls layout editor.");
});
leftPane.register_control(leftPane.add_select_button({
.key = "Touch Targeting",
.getValue =
[] {
return touch_targeting_label(
getSettings().game.touchTargeting.getValue());
},
.isDisabled =
[] { return !getSettings().game.enableTouchControls; },
.isModified =
[] {
const auto& targeting =
getSettings().game.touchTargeting;
return targeting.getValue() !=
targeting.getDefaultValue();
},
}),
rightPane, [](Pane& pane) {
pane.clear();
for (int i = 0; i < static_cast<int>(kTouchTargetingLabels.size()); ++i) {
pane.add_button({
.text = kTouchTargetingLabels[i],
.isSelected =
[i] {
return getSettings().game.touchTargeting.getValue() ==
static_cast<TouchTargeting>(i);
},
})
.on_pressed([i] {
mDoAud_seStartMenu(kSoundItemChange);
getSettings().game.touchTargeting.setValue(
static_cast<TouchTargeting>(i));
config::Save();
});
}
pane.add_rml(fmt::format("<br/>Hybrid: {}<br/>Hold: {}<br/>Switch: {}",
kTouchTargetingDescriptions[0], kTouchTargetingDescriptions[1],
kTouchTargetingDescriptions[2]));
});
config_percent_select(leftPane, rightPane, getSettings().game.touchCameraXSensitivity,
"Touch Camera X Sensitivity",
"Adjusts touch camera horizontal sensitivity.<br/><br/>Applies to touch input only.",
+65 -26
View File
@@ -485,43 +485,71 @@ void TouchControls::set_control_pressed(Control control, bool pressed) {
mLastLTapTime = {};
break;
}
if (pressed && (mLLatched || mManualLLatched)) {
switch (getSettings().game.touchTargeting.getValue()) {
case TouchTargeting::Hold:
mLPressed = pressed;
mLLatched = false;
mManualLLatched = false;
mLPressed = false;
mLReleasePending = true;
mLReleasePending = false;
mLPressStartTime = {};
mLastLTapTime = {};
set_control_visual(control, false);
} else if (pressed) {
const auto now = clock::now();
if (!player_attention_locked() && mLastLTapTime != clock::time_point{} &&
now - mLastLTapTime <= kLDoubleTapWindow)
{
mManualLLatched = true;
break;
case TouchTargeting::Switch:
if (pressed) {
const bool wasLatched = mLPressed || mLLatched || mManualLLatched;
mLPressed = false;
mLLatched = false;
mManualLLatched = !wasLatched;
mLReleasePending = true;
} else {
mLPressed = false;
mLLatched = false;
mLReleasePending = false;
}
mLPressStartTime = {};
mLastLTapTime = {};
break;
case TouchTargeting::Hybrid:
default:
if (pressed && (mLLatched || mManualLLatched)) {
mLLatched = false;
mManualLLatched = false;
mLPressed = false;
mLReleasePending = true;
mLPressStartTime = {};
mLastLTapTime = {};
set_control_visual(control, false);
} else if (pressed) {
const auto now = clock::now();
if (!player_attention_locked() && mLastLTapTime != clock::time_point{} &&
now - mLastLTapTime <= kLDoubleTapWindow)
{
mManualLLatched = true;
mLPressed = false;
mLReleasePending = true;
mLPressStartTime = {};
mLastLTapTime = {};
} else if (!mLReleasePending) {
mLPressed = true;
mLPressStartTime = now;
}
} else if (!mLReleasePending) {
mLPressed = true;
mLPressStartTime = now;
mLPressed = false;
}
} else if (!mLReleasePending) {
mLPressed = false;
}
if (!pressed) {
const auto now = clock::now();
if (!mLReleasePending) {
const bool wasQuickTap = mLPressStartTime != clock::time_point{} &&
now - mLPressStartTime <= kLDoubleTapWindow;
mLastLTapTime = wasQuickTap ? now : clock::time_point{};
if (!pressed) {
const auto now = clock::now();
if (!mLReleasePending) {
const bool wasQuickTap = mLPressStartTime != clock::time_point{} &&
now - mLPressStartTime <= kLDoubleTapWindow;
mLastLTapTime = wasQuickTap ? now : clock::time_point{};
}
mLPressStartTime = {};
mLReleasePending = false;
}
mLPressStartTime = {};
mLReleasePending = false;
}
if (!pressed && !player_attention_locked()) {
mLLatched = false;
if (!pressed && !player_attention_locked()) {
mLLatched = false;
}
break;
}
break;
case Control::R:
@@ -635,6 +663,17 @@ void TouchControls::apply_control_transform(Control control) noexcept {
}
void TouchControls::sync_l_lock_state() noexcept {
const auto targeting = getSettings().game.touchTargeting.getValue();
if (targeting == TouchTargeting::Hold) {
mLLatched = false;
mManualLLatched = false;
return;
}
if (targeting == TouchTargeting::Switch) {
mLLatched = false;
return;
}
if (player_attention_locked()) {
if (mLPressed) {
mLLatched = true;