From 7dfbff45d141be72b1f5935ea9b4e750d32c1d81 Mon Sep 17 00:00:00 2001 From: funman300 Date: Sat, 25 Apr 2026 22:39:08 -0700 Subject: [PATCH] feat(engine): add HelpPlugin (H/?) and Challenge cleared toast - HelpPlugin: full-window cheat sheet listing every keybinding, toggled with H or ?. Three unit tests cover open/close/slash. - AnimationPlugin: ChallengeAdvancedEvent now surfaces as a 3-second "Challenge N cleared!" toast. Co-Authored-By: Claude Opus 4.7 --- docs/SESSION_HANDOFF.md | 16 ++- solitaire_app/src/main.rs | 3 +- solitaire_engine/src/animation_plugin.rs | 17 +++ solitaire_engine/src/help_plugin.rs | 163 +++++++++++++++++++++++ solitaire_engine/src/lib.rs | 2 + 5 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 solitaire_engine/src/help_plugin.rs diff --git a/docs/SESSION_HANDOFF.md b/docs/SESSION_HANDOFF.md index 1d69396..77a60e6 100644 --- a/docs/SESSION_HANDOFF.md +++ b/docs/SESSION_HANDOFF.md @@ -2,7 +2,7 @@ > Last updated: 2026-04-25 > Branch: `master` — pushed to https://git.aleshym.co/funman300/Rusty_Solitare.git -> Test count: **222 passing** (83 core + 54 data + 85 engine), `cargo clippy --workspace -- -D warnings` clean +> Test count: **225 passing** (83 core + 54 data + 88 engine), `cargo clippy --workspace -- -D warnings` clean --- @@ -148,14 +148,18 @@ All sub-phases (3A–3F) done. Plugins: `GamePlugin`, `TablePlugin`, `CardPlugin - `StatsPlugin` overlay (**S**) appends an "Unlocks" subsection (card backs / backgrounds, sorted/deduped, "None" when empty) and a live "Time Attack" panel showing remaining minutes/seconds + wins while a session is active. - Helper `format_id_list` factored out + tested. +### Phase 7 (part 1) — Help Overlay + Challenge Toast ✅ COMPLETE + +- `HelpPlugin`: **H** or `?` toggles a full-window cheat sheet listing all keybindings (gameplay, mode hotkeys, overlays). 3 unit tests. +- `AnimationPlugin` now surfaces `ChallengeAdvancedEvent` as a 3-second toast ("Challenge N cleared!"). + ## What Is Next -### Phase 7 — Audio + Polish +### Phase 7 (part 2+) — Audio + Pause Menu -- Audio (`kira`): card deal/flip/place/invalid SFX, win fanfare, ambient loop. Volume sliders in a Settings overlay. -- Onboarding: first-run hint overlay (rules summary + key list). -- Pause menu (Esc currently logs a placeholder). -- Optional: ChallengeAdvancedEvent → toast in `AnimationPlugin`. +- Audio (`kira`): card deal/flip/place/invalid SFX, win fanfare, ambient loop. Volume sliders in a Settings overlay. **Blocker:** asset files are not yet in the repo; sourcing/recording these is the first step. +- Pause menu: Esc currently logs a placeholder. Likely a small overlay similar to `HelpPlugin` with a `Paused` resource that gates `Time::delta_secs` propagation in `tick_elapsed_time` / `advance_time_attack`. +- Onboarding: first-run banner pointing at the **H**/`?` cheat sheet (single-shot via `Settings.first_run_complete`). ### Phase 8 — Sync diff --git a/solitaire_app/src/main.rs b/solitaire_app/src/main.rs index 6305190..808fb5c 100644 --- a/solitaire_app/src/main.rs +++ b/solitaire_app/src/main.rs @@ -1,7 +1,7 @@ use bevy::prelude::*; use solitaire_engine::{ AchievementPlugin, AnimationPlugin, CardPlugin, ChallengePlugin, DailyChallengePlugin, - GamePlugin, InputPlugin, ProgressPlugin, StatsPlugin, TablePlugin, TimeAttackPlugin, + GamePlugin, HelpPlugin, InputPlugin, ProgressPlugin, StatsPlugin, TablePlugin, TimeAttackPlugin, WeeklyGoalsPlugin, }; @@ -29,5 +29,6 @@ fn main() { .add_plugins(WeeklyGoalsPlugin) .add_plugins(ChallengePlugin) .add_plugins(TimeAttackPlugin) + .add_plugins(HelpPlugin) .run(); } diff --git a/solitaire_engine/src/animation_plugin.rs b/solitaire_engine/src/animation_plugin.rs index 2b382e8..12a0684 100644 --- a/solitaire_engine/src/animation_plugin.rs +++ b/solitaire_engine/src/animation_plugin.rs @@ -7,6 +7,7 @@ use bevy::prelude::*; use crate::achievement_plugin::display_name_for; use crate::card_plugin::CardEntity; +use crate::challenge_plugin::ChallengeAdvancedEvent; use crate::daily_challenge_plugin::DailyChallengeCompletedEvent; use crate::events::{AchievementUnlockedEvent, GameWonEvent}; use crate::game_plugin::GameMutation; @@ -24,6 +25,7 @@ const LEVELUP_TOAST_SECS: f32 = 3.0; const DAILY_TOAST_SECS: f32 = 3.0; const WEEKLY_TOAST_SECS: f32 = 3.0; const TIME_ATTACK_TOAST_SECS: f32 = 5.0; +const CHALLENGE_TOAST_SECS: f32 = 3.0; const CASCADE_STAGGER: f32 = 0.05; const CASCADE_DURATION: f32 = 0.5; @@ -62,6 +64,7 @@ impl Plugin for AnimationPlugin { .add_event::() .add_event::() .add_event::() + .add_event::() .add_systems( Update, ( @@ -72,6 +75,7 @@ impl Plugin for AnimationPlugin { handle_daily_toast, handle_weekly_toast, handle_time_attack_toast, + handle_challenge_toast, tick_toasts, ) .after(GameMutation), @@ -198,6 +202,19 @@ fn handle_time_attack_toast( } } +fn handle_challenge_toast( + mut commands: Commands, + mut events: EventReader, +) { + for ev in events.read() { + spawn_toast( + &mut commands, + format!("Challenge {} cleared!", ev.previous_index.saturating_add(1)), + CHALLENGE_TOAST_SECS, + ); + } +} + fn tick_toasts( mut commands: Commands, time: Res