//! Routes game-request events to `solitaire_core::GameState` and emits //! state-change notifications. use std::time::{SystemTime, UNIX_EPOCH}; use bevy::prelude::*; use solitaire_core::game_state::{DrawMode, GameState}; use crate::events::{ DrawRequestEvent, GameWonEvent, MoveRequestEvent, NewGameRequestEvent, StateChangedEvent, UndoRequestEvent, }; use crate::resources::{DragState, GameStateResource, SyncStatusResource}; /// System set for `GamePlugin`'s state-mutating systems. Downstream plugins /// that read the resulting `StateChangedEvent` should schedule themselves /// `.after(GameMutation)` so updates propagate within a single frame. #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] pub struct GameMutation; /// Registers game resources, events, and the systems that route user intent /// (events) into mutations on `GameState`. pub struct GamePlugin; impl Plugin for GamePlugin { fn build(&self, app: &mut App) { app.insert_resource(GameStateResource(GameState::new( seed_from_system_time(), DrawMode::DrawOne, ))) .init_resource::() .init_resource::() .add_event::() .add_event::() .add_event::() .add_event::() .add_event::() .add_event::() .add_event::() .add_event::() .add_event::() .add_systems( Update, ( handle_new_game, handle_draw, handle_move, handle_undo, ) .chain() .in_set(GameMutation), ) .add_systems(Update, tick_elapsed_time); } } /// Pure, testable helper. Updates `elapsed_seconds` and drains the /// fractional accumulator into whole-second ticks. No-op when `is_won`. pub fn advance_elapsed( elapsed_seconds: &mut u64, accumulator: &mut f32, delta_secs: f32, is_won: bool, ) { if is_won { return; } *accumulator += delta_secs; while *accumulator >= 1.0 { *elapsed_seconds = elapsed_seconds.saturating_add(1); *accumulator -= 1.0; } } /// Increment `GameState.elapsed_seconds` once per real-world second while /// the game is in progress (not won) and not paused. Stops counting on /// win so the final time reflects how long the player took to solve the /// deal; stops while the pause overlay is open. fn tick_elapsed_time( time: Res