feat(engine): playability improvements — rounds 7–9 (#40–#64)
Round 7 — Input & feedback
- H key cycles hints; F1 opens help (conflict resolved)
- N key cancels active Time Attack session
- Hint text distinguishes "draw from stock" vs "recycle waste"
- Forfeit (G) clears AutoCompleteState so chime does not bleed into new deal
- Zen mode timer clears immediately on Z press
- HUD shows recycle count in both draw modes
- Settings scroll position persisted across open/close
Round 8 — Polish & clarity
- Undo unavailable fires "Nothing to undo" toast
- Streak-break toast on forfeit/abandon when streak > 1
- F11 fullscreen toggle with toast; documented in help and home screens
- H-after-win, new-game countdown expiry, Tab-no-cards toasts
- Win cascade duration/stagger scales with animation speed setting
- Draw-Three cycle counter HUD ("Cycle: N/3")
- Forfeit requires G×2 confirmation within 3 s (mirrors N key)
Round 9 — Game feel & information
- Escape dismisses game-over/stuck overlay (PausePlugin skips Escape when overlay visible)
- Shake animation on rejected drag before snap-back
- Forfeit countdown cancels when any other key is pressed (U/H/D/Z/Space)
- Tab wrap-around fires "Back to first card" toast
- HUD selection indicator shows active Tab-selected pile in yellow
- Challenge time-limit HUD turns orange < 60s, red < 30s
- Win summary shows XP breakdown (+50 base, +25 no-undo, +N speed)
- Game-over overlay: "No more moves available" with clear N/Escape/G instructions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@ use chrono::{Local, NaiveDate};
|
||||
use solitaire_data::{daily_seed_for, save_progress_to};
|
||||
use solitaire_sync::ChallengeGoal;
|
||||
|
||||
use crate::events::{GameWonEvent, NewGameRequestEvent, XpAwardedEvent};
|
||||
use crate::events::{GameWonEvent, InfoToastEvent, NewGameRequestEvent, XpAwardedEvent};
|
||||
use crate::game_plugin::GameMutation;
|
||||
use crate::progress_plugin::{ProgressResource, ProgressStoragePath, ProgressUpdate};
|
||||
use crate::resources::GameStateResource;
|
||||
@@ -143,6 +143,7 @@ fn poll_server_challenge(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_daily_completion(
|
||||
mut wins: EventReader<GameWonEvent>,
|
||||
daily: Res<DailyChallengeResource>,
|
||||
@@ -151,6 +152,7 @@ fn handle_daily_completion(
|
||||
path: Res<ProgressStoragePath>,
|
||||
mut completed: EventWriter<DailyChallengeCompletedEvent>,
|
||||
mut xp_awarded: EventWriter<XpAwardedEvent>,
|
||||
mut toast: EventWriter<InfoToastEvent>,
|
||||
) {
|
||||
for ev in wins.read() {
|
||||
if game.0.seed != daily.seed {
|
||||
@@ -182,6 +184,7 @@ fn handle_daily_completion(
|
||||
date: daily.date,
|
||||
streak: progress.0.daily_challenge_streak,
|
||||
});
|
||||
toast.send(InfoToastEvent("Daily challenge complete! +100 XP".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user