feat(engine): skip splash on subsequent launches
CI / Test & Lint (push) Failing after 34s
CI / Release Build (push) Has been skipped

The 1.6 s brand beat is delightful on first launch and tedious on
every subsequent one. spawn_splash now reads SettingsResource and
returns early when first_run_complete is true — the player has
already seen the splash at least once and the onboarding flow that
follows it, so dropping straight into gameplay is the right move.

Reuses the existing first_run_complete signal rather than introducing
a separate splash_seen field; the two states ("I've been here") line
up naturally and avoid carrying a one-shot flag forever.

The first run, a save reset (settings.json deleted), or a headless
test fixture that doesn't register SettingsResource all still see the
full splash — Option<Res<SettingsResource>> defaults to "show" when
absent, so the existing test fixture observes the same spawn it
always did.

Two new tests pin the split: splash_skipped_when_first_run_complete
asserts no SplashRoot spawns when settings say so, and
splash_still_shows_when_first_run_incomplete asserts the first-run
path is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-01 03:15:36 +00:00
parent 3ef4ecb747
commit 912b08c719
+61 -1
View File
@@ -42,6 +42,7 @@ use bevy::input::touch::Touches;
use bevy::prelude::*;
use crate::font_plugin::FontResource;
use crate::settings_plugin::SettingsResource;
use crate::ui_theme::{
ACCENT_PRIMARY, BG_BASE, MOTION_SPLASH_FADE_SECS, MOTION_SPLASH_TOTAL_SECS, TEXT_SECONDARY,
TYPE_CAPTION, TYPE_DISPLAY, VAL_SPACE_2, Z_SPLASH,
@@ -108,7 +109,25 @@ struct SplashSubtitle;
/// at full alpha (the first `advance_splash` tick will overwrite the
/// alpha based on age), centres a "Solitaire Quest" title in
/// [`ACCENT_PRIMARY`], and pins a small build-version line below.
fn spawn_splash(mut commands: Commands, font_res: Option<Res<FontResource>>) {
///
/// **Skipped on subsequent launches.** If `SettingsResource` reports
/// `first_run_complete == true`, the player has already seen the brand
/// beat at least once and we go straight to gameplay — having to wait
/// 1.6 s on every launch wears thin fast. The splash still shows on
/// first run, after a save reset (settings.json deleted), and under
/// `MinimalPlugins` (no `SettingsResource` registered) so the test
/// fixture observes the same spawn it always did.
fn spawn_splash(
mut commands: Commands,
font_res: Option<Res<FontResource>>,
settings: Option<Res<SettingsResource>>,
) {
if let Some(settings) = settings.as_deref()
&& settings.0.first_run_complete
{
return;
}
let font_handle = font_res.map(|f| f.0.clone()).unwrap_or_default();
let title_font = TextFont {
font: font_handle.clone(),
@@ -371,6 +390,47 @@ mod tests {
);
}
#[test]
fn splash_skipped_when_first_run_complete() {
use solitaire_data::Settings;
let mut app = App::new();
app.add_plugins(MinimalPlugins).add_plugins(SplashPlugin);
app.init_resource::<ButtonInput<KeyCode>>();
app.init_resource::<ButtonInput<MouseButton>>();
// Insert a SettingsResource that says "I've been here before"
// before any Startup system runs. spawn_splash should observe
// first_run_complete and decline to spawn the overlay.
app.insert_resource(SettingsResource(Settings {
first_run_complete: true,
..Settings::default()
}));
app.update();
assert_eq!(
count_splash_roots(&mut app),
0,
"SplashRoot must NOT spawn on subsequent launches"
);
}
#[test]
fn splash_still_shows_when_first_run_incomplete() {
use solitaire_data::Settings;
let mut app = App::new();
app.add_plugins(MinimalPlugins).add_plugins(SplashPlugin);
app.init_resource::<ButtonInput<KeyCode>>();
app.init_resource::<ButtonInput<MouseButton>>();
app.insert_resource(SettingsResource(Settings {
first_run_complete: false,
..Settings::default()
}));
app.update();
assert_eq!(
count_splash_roots(&mut app),
1,
"SplashRoot must spawn for first-run players (first_run_complete = false)"
);
}
#[test]
fn splash_despawns_after_total_duration() {
let mut app = headless_app();