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:
funman300
2026-04-28 22:13:10 +00:00
parent ffc79447d4
commit 1ec2593137
6 changed files with 207 additions and 4 deletions
+9
View File
@@ -361,6 +361,15 @@ impl GameState {
/// Scans tableau piles 06 in order, returning the first top card that
/// can be placed on any foundation pile. The scan order ensures Aces are
/// resolved before higher ranks that depend on them.
///
/// # Precondition
///
/// This function is only called when `is_auto_completable` is `true`.
/// Auto-completability requires the waste pile to be empty, as enforced by
/// [`check_auto_complete`](Self::check_auto_complete) — it returns `false`
/// whenever `piles[Waste]` is non-empty. Therefore, skipping the waste pile
/// in this scan is intentional and correct: by the time this function is
/// reached, there are guaranteed to be no cards there to move.
pub fn next_auto_complete_move(&self) -> Option<(PileType, PileType)> {
if !self.is_auto_completable || self.is_won {
return None;