diff --git a/solitaire_engine/src/game_plugin.rs b/solitaire_engine/src/game_plugin.rs index f83f800..b1295b6 100644 --- a/solitaire_engine/src/game_plugin.rs +++ b/solitaire_engine/src/game_plugin.rs @@ -168,6 +168,8 @@ fn handle_new_game( font_res: Option>, confirm_screens: Query>, game_over_screens: Query>, + layout: Option>, + mut card_transforms: Query<&mut Transform, With>, ) { for ev in new_game.read() { // If an active game is in progress, intercept and show a confirm dialog. @@ -209,6 +211,24 @@ fn handle_new_game( && let Err(e) = delete_game_state_at(p) { warn!("game_state: failed to delete saved game: {e}"); } + // Snap every existing card sprite to the stock position before the + // deal animation starts. Without this the per-card slide tween reads + // each card's previous-game Transform as its source, which lets a + // careful observer track origin points to deduce where face-down + // cards came from. Funnelling all sprites through the deck position + // hides that information and reads naturally as "dealt from the + // deck." Skipped when LayoutResource isn't present (headless tests). + if let Some(layout) = layout.as_ref() + && let Some(stock) = layout + .0 + .pile_positions + .get(&solitaire_core::pile::PileType::Stock) + { + for mut tx in &mut card_transforms { + tx.translation.x = stock.x; + tx.translation.y = stock.y; + } + } changed.write(StateChangedEvent); } }