feat(engine,core): add elapsed-time tick system and Zen GameMode

Phase 6 part 4 (partial):
- GameState now tracks elapsed_seconds via tick_elapsed_time in GamePlugin
  (per-second increment while not won). Pure helper advance_elapsed makes
  the tick logic directly testable without mocking Bevy Time.
- New GameMode enum (Classic / Zen) on GameState. Zen mode suppresses
  scoring in move_cards and undo. GameState::new_with_mode allows callers
  to construct non-Classic games; the existing GameState::new still
  defaults to Classic. mode is serde(default) for backwards-compatible
  persistence.
- NewGameRequestEvent gains an optional mode field; handle_new_game
  honours it (falling back to the current game's mode when None).
- InputPlugin: pressing Z starts a fresh Zen-mode game.

Time Attack, Challenge mode, level-5 unlock gating, and unlock UI are
still deferred.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-04-25 14:14:57 -07:00
parent 6b793aa2ab
commit 8afb1f3fe5
6 changed files with 79 additions and 12 deletions
+2 -2
View File
@@ -296,7 +296,7 @@ mod tests {
.move_count = 3;
app.world_mut()
.send_event(NewGameRequestEvent { seed: Some(999) });
.send_event(NewGameRequestEvent { seed: Some(999), mode: None });
app.update();
let stats = &app.world().resource::<StatsResource>().0;
@@ -309,7 +309,7 @@ mod tests {
fn new_game_without_moves_does_not_record_abandoned() {
let mut app = headless_app();
app.world_mut()
.send_event(NewGameRequestEvent { seed: Some(42) });
.send_event(NewGameRequestEvent { seed: Some(42), mode: None });
app.update();
let stats = &app.world().resource::<StatsResource>().0;