fix(engine): resolve input coordination bugs in selection/pause/keyboard
- SelectionPlugin: add clear_selection_on_state_change system so undo/move/reject never leave a stale selection pointing at the wrong card - SelectionPlugin: expose SelectionKeySet system set for cross-plugin ordering - PausePlugin: skip Escape→pause when a card is keyboard-selected; toggle_pause now runs before SelectionKeySet so it reads SelectionState before it is cleared - InputPlugin: guard Space→DrawRequestEvent when SelectionState has an active pile so Space executes a card move instead of also drawing from stock - window: enforce 800×600 minimum via WindowResizeConstraints - game_state: add precondition doc to next_auto_complete_move (waste exclusion) - card_plugin: 12 unit tests for constants, face_colour, label_visibility, label_for - pause_plugin: add paused_resource_default and draw_mode_label exhaustiveness tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@ use solitaire_core::card::Suit;
|
||||
use solitaire_core::pile::PileType;
|
||||
|
||||
use crate::card_plugin::CardEntity;
|
||||
use crate::events::{InfoToastEvent, MoveRequestEvent};
|
||||
use crate::events::{InfoToastEvent, MoveRequestEvent, StateChangedEvent};
|
||||
use crate::game_plugin::GameMutation;
|
||||
use crate::input_plugin::{best_destination, best_tableau_destination_for_stack};
|
||||
use crate::layout::LayoutResource;
|
||||
@@ -42,6 +42,13 @@ pub struct SelectionState {
|
||||
pub selected_pile: Option<PileType>,
|
||||
}
|
||||
|
||||
/// System set label for the key-handling system.
|
||||
///
|
||||
/// `PausePlugin` registers `toggle_pause` before this set so it can read
|
||||
/// [`SelectionState`] before `handle_selection_keys` clears it on Escape.
|
||||
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SelectionKeySet;
|
||||
|
||||
/// Marker component placed on the outline sprite used as the keyboard-selection
|
||||
/// highlight.
|
||||
///
|
||||
@@ -59,7 +66,10 @@ impl Plugin for SelectionPlugin {
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
handle_selection_keys.before(GameMutation),
|
||||
handle_selection_keys
|
||||
.in_set(SelectionKeySet)
|
||||
.before(GameMutation),
|
||||
clear_selection_on_state_change.after(GameMutation),
|
||||
update_selection_highlight.after(GameMutation),
|
||||
),
|
||||
);
|
||||
@@ -329,6 +339,20 @@ fn try_foundation_dest(
|
||||
None
|
||||
}
|
||||
|
||||
/// Clears the selection whenever the game state changes.
|
||||
///
|
||||
/// Without this, an undo or a rejected move could leave `selected_pile`
|
||||
/// pointing at a pile whose top card changed, causing the highlight to
|
||||
/// trail a different card than the player expects.
|
||||
fn clear_selection_on_state_change(
|
||||
mut state_events: MessageReader<StateChangedEvent>,
|
||||
mut selection: ResMut<SelectionState>,
|
||||
) {
|
||||
if state_events.read().next().is_some() {
|
||||
selection.selected_pile = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Maintains the `SelectionHighlight` outline sprite.
|
||||
///
|
||||
/// When a pile is selected, a cyan sprite is placed at the selected card's
|
||||
|
||||
Reference in New Issue
Block a user