diff --git a/solitaire_engine/src/achievement_plugin.rs b/solitaire_engine/src/achievement_plugin.rs index 9419a6e..b7f2b92 100644 --- a/solitaire_engine/src/achievement_plugin.rs +++ b/solitaire_engine/src/achievement_plugin.rs @@ -533,9 +533,9 @@ fn spawn_achievements_screen( } let (name_color, desc_color, prefix) = if record.unlocked { - (ACCENT_PRIMARY, TEXT_PRIMARY, "\u{2713} ") + (ACCENT_PRIMARY, TEXT_PRIMARY, "+ ") } else { - (TEXT_DISABLED, TEXT_DISABLED, "\u{25CB} ") + (TEXT_DISABLED, TEXT_DISABLED, "- ") }; let tooltip_text = tooltip_for_row(record.unlocked, def); diff --git a/solitaire_engine/src/card_plugin.rs b/solitaire_engine/src/card_plugin.rs index e9ccd82..1a521c4 100644 --- a/solitaire_engine/src/card_plugin.rs +++ b/solitaire_engine/src/card_plugin.rs @@ -1484,7 +1484,7 @@ fn update_stock_empty_indicator( // --------------------------------------------------------------------------- // Stock-pile remaining-count badge // -// Shows a small "·N" chip pinned to the top-right corner of the stock pile so +// Shows a small "N" chip pinned to the top-right corner of the stock pile so // the player can see how many cards remain before the next recycle. The // existing `StockEmptyLabel` (`↺` overlay) covers the empty-stock case, so // the badge hides itself when the stock has zero cards — the two indicators @@ -1562,7 +1562,7 @@ fn spawn_stock_count_badge( .with_children(|b| { b.spawn(( StockCountBadgeText, - Text2d::new(format!("·{count}")), + Text2d::new(format!("{count}")), text_font, TextColor(STOCK_BADGE_FG), // Slightly above the chip background so the digits aren't @@ -1624,7 +1624,7 @@ fn update_stock_count_badge( if let Ok(badge_children) = children.get(entity) { for child in badge_children.iter() { if let Ok(mut text) = texts.get_mut(child) { - let new = format!("·{count}"); + let new = format!("{count}"); if text.0 != new { text.0 = new; } @@ -2811,7 +2811,7 @@ mod tests { // First update inside `app()` runs the spawn path; run one more to // confirm the in-place update path is also stable. app.update(); - assert_eq!(stock_badge_text(&mut app), "·24"); + assert_eq!(stock_badge_text(&mut app), "24"); assert!(matches!(stock_badge_visibility(&mut app), Visibility::Inherited)); } @@ -2837,7 +2837,7 @@ mod tests { // initial 24) and assert the badge text follows. let mut app = app(); // Sanity-check the starting count. - assert_eq!(stock_badge_text(&mut app), "·24"); + assert_eq!(stock_badge_text(&mut app), "24"); { let mut game = app.world_mut().resource_mut::(); if let Some(stock) = game.0.piles.get_mut(&PileType::Stock) { @@ -2845,7 +2845,7 @@ mod tests { } } app.update(); - assert_eq!(stock_badge_text(&mut app), "·23"); + assert_eq!(stock_badge_text(&mut app), "23"); assert!(matches!(stock_badge_visibility(&mut app), Visibility::Inherited)); } diff --git a/solitaire_engine/src/game_plugin.rs b/solitaire_engine/src/game_plugin.rs index 4784325..a77354d 100644 --- a/solitaire_engine/src/game_plugin.rs +++ b/solitaire_engine/src/game_plugin.rs @@ -11,6 +11,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use bevy::prelude::*; use bevy::tasks::{futures_lite::future, AsyncComputeTaskPool, Task}; +use bevy::window::AppLifecycle; use chrono::Utc; use solitaire_core::game_state::{DrawMode, GameMode, GameState}; use solitaire_core::pile::PileType; @@ -200,6 +201,7 @@ impl Plugin for GamePlugin { .add_message::() .add_message::() .add_message::() + .add_message::() .add_systems( Update, poll_pending_new_game_seed.before(GameMutation), @@ -259,20 +261,37 @@ pub fn advance_elapsed( /// timer doesn't tick before the player commits to a deal; stops while /// the onboarding modal is visible so a new player's first-game time /// isn't inflated by reading the tutorial. +/// +/// On Android the first frame after the app is resumed from background +/// can carry a very large `delta_secs` equal to the entire suspension +/// period. `skip_next_delta` is set to `true` on `WillSuspend` / +/// `Suspended` so that frame's delta is dropped instead of applied. +#[allow(clippy::too_many_arguments)] fn tick_elapsed_time( time: Res