From 55c235b55fea9b77a5127b6867f55cecdac2089e Mon Sep 17 00:00:00 2001 From: funman300 Date: Wed, 6 May 2026 19:35:04 -0700 Subject: [PATCH] =?UTF-8?q?fix(engine):=20drop=20duplicate=20"You=20Win"?= =?UTF-8?q?=20toast=20=E2=80=94=20WinSummary=20modal=20owns=20the=20celebr?= =?UTF-8?q?ation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The post-win UI was firing TWO celebration surfaces on every GameWonEvent: - animation_plugin::handle_win_cascade spawned a 4-second toast: "You Win! Score: {score} Time: {m}:{ss}" - win_summary_plugin spawned the proper "You Won!" modal with score breakdown, time bonus, achievements unlocked, XP earned, and a Play Again button Both rendered on top of each other — in screenshots the toast banner was partially clipped behind the modal card, peeking out on either side. The toast predates the WinSummary modal; the modal carries strictly more information so the toast is dead weight. handle_win_cascade keeps the cards-fly-off animation (MotionCurve::Expressive cascade with per-card rotation drift) — that's the visual celebration, distinct from the textual celebration the modal owns. The system still gates on the same GameWonEvent message reader; it just doesn't write a toast afterward. WIN_TOAST_SECS const removed (no remaining callers). Workspace: 1172 passing tests / 0 failing. cargo clippy --workspace --all-targets -- -D warnings clean. Co-Authored-By: Claude Opus 4.7 --- solitaire_engine/src/animation_plugin.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/solitaire_engine/src/animation_plugin.rs b/solitaire_engine/src/animation_plugin.rs index 205e79a..af9399c 100644 --- a/solitaire_engine/src/animation_plugin.rs +++ b/solitaire_engine/src/animation_plugin.rs @@ -61,7 +61,6 @@ fn anim_speed_to_secs(speed: &AnimSpeed) -> f32 { scaled_duration(MOTION_SLIDE_SECS, *speed) } -const WIN_TOAST_SECS: f32 = 4.0; const ACHIEVEMENT_TOAST_SECS: f32 = 3.0; const LEVELUP_TOAST_SECS: f32 = 3.0; const DAILY_TOAST_SECS: f32 = 3.0; @@ -266,9 +265,15 @@ fn handle_win_cascade( layout: Option>, settings: Option>, ) { - let Some(ev) = events.read().next() else { + // Drain the event reader; the cascade visual is the only thing + // this system contributes — the post-win "You Won!" modal + // (`win_summary_plugin`) consumes the same `GameWonEvent` and + // carries score / time / achievements / XP itself, so a duplicate + // toast saying "You Win! Score X Time Y" rendered behind the modal + // in earlier builds. Removed. + if events.read().next().is_none() { return; - }; + } let margin = layout.as_ref().map_or(800.0, |l| l.0.card_size.x * 8.0); @@ -284,11 +289,6 @@ fn handle_win_cascade( Vec3::new(-margin, 0.0, 300.0), ]; - let m = ev.time_seconds / 60; - let s = ev.time_seconds % 60; - let win_msg = format!("You Win! Score: {} Time: {m}:{s:02}", ev.score); - spawn_toast(&mut commands, win_msg, WIN_TOAST_SECS); - let step = settings .as_ref() .map_or(CASCADE_STAGGER_NORMAL, |s| cascade_step_secs(s.0.animation_speed));