fix(engine): prevent stock-tap from toggling HUD on Android
Every draw-from-stock tap was also firing the HUD auto-hide toggle because the stock pile is not an ActionButton and toggle_hud_on_tap had no way to know the tap was consumed by game logic. Add GameInputConsumedResource(bool): handle_touch_stock_tap sets it on TouchPhase::Started when a draw fires; toggle_hud_on_tap checks and clears it on TouchPhase::Ended, treating it as equivalent to started_on_button so the HUD stays put. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -45,7 +45,7 @@ use crate::layout::LayoutSystem;
|
||||
use crate::pause_plugin::PausedResource;
|
||||
use crate::resources::GameStateResource;
|
||||
#[cfg(target_os = "android")]
|
||||
use crate::resources::DragState;
|
||||
use crate::resources::{DragState, GameInputConsumedResource};
|
||||
use crate::selection_plugin::SelectionState;
|
||||
use crate::time_attack_plugin::TimeAttackResource;
|
||||
use crate::ui_focus::{FocusGroup, Focusable};
|
||||
@@ -2492,6 +2492,7 @@ fn toggle_hud_on_tap(
|
||||
mut tracker: ResMut<HudTapTracker>,
|
||||
mut hud_vis: ResMut<HudVisibility>,
|
||||
buttons: Query<&Interaction, With<ActionButton>>,
|
||||
mut game_consumed: ResMut<GameInputConsumedResource>,
|
||||
) {
|
||||
use bevy::input::touch::TouchPhase;
|
||||
if !scrims.is_empty() || paused.is_some_and(|p| p.0) {
|
||||
@@ -2502,6 +2503,7 @@ fn toggle_hud_on_tap(
|
||||
for _ in touch_events.read() {}
|
||||
tracker.start_pos = None;
|
||||
tracker.started_on_button = false;
|
||||
game_consumed.0 = false;
|
||||
return;
|
||||
}
|
||||
for event in touch_events.read() {
|
||||
@@ -2515,7 +2517,13 @@ fn toggle_hud_on_tap(
|
||||
buttons.iter().any(|i| *i != Interaction::None);
|
||||
}
|
||||
TouchPhase::Ended if drag.is_idle() => {
|
||||
let on_button = tracker.started_on_button;
|
||||
// Also treat taps where game logic consumed the touch (e.g.
|
||||
// drawing from stock) as "on button" so they don't toggle
|
||||
// the HUD. The flag is set on TouchPhase::Started by the
|
||||
// input system that consumed the tap and must be cleared here
|
||||
// regardless of whether we toggle.
|
||||
let on_button = tracker.started_on_button || game_consumed.0;
|
||||
game_consumed.0 = false;
|
||||
if let Some(start) = tracker.start_pos.take() {
|
||||
if !on_button && (event.position - start).length() < HUD_TAP_SLOP_PX {
|
||||
*hud_vis = match *hud_vis {
|
||||
@@ -2532,6 +2540,7 @@ fn toggle_hud_on_tap(
|
||||
TouchPhase::Canceled => {
|
||||
tracker.start_pos = None;
|
||||
tracker.started_on_button = false;
|
||||
game_consumed.0 = false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ use crate::game_plugin::{ConfirmNewGameScreen, GameMutation, RestorePromptScreen
|
||||
use crate::pause_plugin::PausedResource;
|
||||
use crate::progress_plugin::ProgressResource;
|
||||
use crate::layout::{Layout, LayoutResource};
|
||||
use crate::resources::{DragState, GameStateResource, HintCycleIndex};
|
||||
use crate::resources::{DragState, GameInputConsumedResource, GameStateResource, HintCycleIndex};
|
||||
use crate::selection_plugin::SelectionState;
|
||||
use crate::time_attack_plugin::TimeAttackResource;
|
||||
|
||||
@@ -95,6 +95,7 @@ impl Plugin for InputPlugin {
|
||||
app.init_resource::<HintCycleIndex>()
|
||||
.init_resource::<HintSolverConfig>()
|
||||
.init_resource::<crate::pending_hint::PendingHintTask>()
|
||||
.init_resource::<GameInputConsumedResource>()
|
||||
.add_message::<StartZenRequestEvent>()
|
||||
.add_message::<InfoToastEvent>()
|
||||
.add_message::<ForfeitRequestEvent>()
|
||||
@@ -501,6 +502,7 @@ fn handle_touch_stock_tap(
|
||||
layout: Option<Res<LayoutResource>>,
|
||||
drag: Res<DragState>,
|
||||
mut draw: MessageWriter<DrawRequestEvent>,
|
||||
mut game_consumed: ResMut<GameInputConsumedResource>,
|
||||
) {
|
||||
if paused.is_some_and(|p| p.0) {
|
||||
return;
|
||||
@@ -522,6 +524,7 @@ fn handle_touch_stock_tap(
|
||||
};
|
||||
if point_in_rect(world, stock_pos, layout.0.card_size) {
|
||||
draw.write(DrawRequestEvent);
|
||||
game_consumed.0 = true;
|
||||
break; // one draw per tap frame
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,13 @@ pub struct HintCycleIndex(pub usize);
|
||||
#[derive(Resource, Debug, Clone, Default)]
|
||||
pub struct SettingsScrollPos(pub f32);
|
||||
|
||||
/// Set to `true` by an input system when a touch tap is consumed by game logic
|
||||
/// (e.g. drawing from stock). `toggle_hud_on_tap` checks this flag on
|
||||
/// `TouchPhase::Ended` and skips the HUD visibility toggle when set, then
|
||||
/// resets it to `false` so subsequent taps behave normally.
|
||||
#[derive(Resource, Debug, Clone, Default)]
|
||||
pub struct GameInputConsumedResource(pub bool);
|
||||
|
||||
/// Shared Tokio runtime used by all async-task closures that need HTTP I/O.
|
||||
///
|
||||
/// Bevy's `AsyncComputeTaskPool` uses `async-executor` (not Tokio), so spawned
|
||||
|
||||
Reference in New Issue
Block a user