fix(engine): enable take-from-foundation for restored and startup games
Android Release / build-apk (push) Successful in 3m42s

GameState serializes take_from_foundation=false (the core default),
so saved games on disk and direct-loaded states never had the setting
applied from SettingsResource — only freshly dealt games did.

Two fixes:
- sync_settings_to_game: new system that reads SettingsChangedEvent
  and patches game.0.take_from_foundation on every settings change
  (covers initial settings load at startup and in-session toggles)
- handle_restore_prompt: apply settings immediately after game.0 =
  restored so the Continue path also respects the current setting
- Register SettingsChangedEvent in GamePlugin::build (idempotent with
  SettingsPlugin) so the message is available in headless test apps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-19 15:26:42 -07:00
parent 6cd8c6c013
commit fae5933d29
+25
View File
@@ -202,6 +202,8 @@ impl Plugin for GamePlugin {
.add_message::<FoundationCompletedEvent>() .add_message::<FoundationCompletedEvent>()
.add_message::<InfoToastEvent>() .add_message::<InfoToastEvent>()
.add_message::<AppLifecycle>() .add_message::<AppLifecycle>()
// add_message is idempotent; SettingsPlugin also registers this.
.add_message::<crate::settings_plugin::SettingsChangedEvent>()
.add_systems( .add_systems(
Update, Update,
poll_pending_new_game_seed.before(GameMutation), poll_pending_new_game_seed.before(GameMutation),
@@ -228,6 +230,7 @@ impl Plugin for GamePlugin {
// GameMutation flow. // GameMutation flow.
.add_systems(Update, spawn_restore_prompt_if_pending) .add_systems(Update, spawn_restore_prompt_if_pending)
.add_systems(Update, handle_restore_prompt.before(GameMutation)) .add_systems(Update, handle_restore_prompt.before(GameMutation))
.add_systems(Update, sync_settings_to_game.before(GameMutation))
.init_resource::<AutoSaveTimer>() .init_resource::<AutoSaveTimer>()
.add_systems(Update, tick_elapsed_time) .add_systems(Update, tick_elapsed_time)
.add_systems(Update, auto_save_game_state) .add_systems(Update, auto_save_game_state)
@@ -235,6 +238,23 @@ impl Plugin for GamePlugin {
} }
} }
/// Forwards `take_from_foundation` from [`SettingsResource`] to the live
/// [`GameStateResource`] every time [`SettingsChangedEvent`] fires.
///
/// This covers two cases that the new-game path misses:
/// 1. The initial settings load at startup: saves on disk default to `false`
/// but `Settings` defaults to `true`; the event fires once when the
/// settings file is first read.
/// 2. A user toggling the setting mid-session in the Settings panel.
fn sync_settings_to_game(
mut events: MessageReader<crate::settings_plugin::SettingsChangedEvent>,
mut game: ResMut<GameStateResource>,
) {
for ev in events.read() {
game.0.take_from_foundation = ev.0.take_from_foundation;
}
}
/// Pure, testable helper. Updates `elapsed_seconds` and drains the /// Pure, testable helper. Updates `elapsed_seconds` and drains the
/// fractional accumulator into whole-second ticks. No-op when `is_won`. /// fractional accumulator into whole-second ticks. No-op when `is_won`.
pub fn advance_elapsed( pub fn advance_elapsed(
@@ -614,6 +634,7 @@ fn handle_restore_prompt(
new_game_buttons: Query<&Interaction, (With<RestoreNewGameButton>, Changed<Interaction>)>, new_game_buttons: Query<&Interaction, (With<RestoreNewGameButton>, Changed<Interaction>)>,
mut pending: ResMut<PendingRestoredGame>, mut pending: ResMut<PendingRestoredGame>,
mut game: ResMut<GameStateResource>, mut game: ResMut<GameStateResource>,
settings: Option<Res<crate::settings_plugin::SettingsResource>>,
mut changed: MessageWriter<StateChangedEvent>, mut changed: MessageWriter<StateChangedEvent>,
mut new_game: MessageWriter<NewGameRequestEvent>, mut new_game: MessageWriter<NewGameRequestEvent>,
mut launch_home_shown: Option<ResMut<crate::home_plugin::LaunchHomeShown>>, mut launch_home_shown: Option<ResMut<crate::home_plugin::LaunchHomeShown>>,
@@ -639,6 +660,10 @@ fn handle_restore_prompt(
let resolved = if key_continue || click_continue { let resolved = if key_continue || click_continue {
if let Some(restored) = pending.0.take() { if let Some(restored) = pending.0.take() {
game.0 = restored; game.0 = restored;
// Patch setting that serialized with the old core default of `false`.
if let Some(s) = settings.as_ref() {
game.0.take_from_foundation = s.0.take_from_foundation;
}
changed.write(StateChangedEvent); changed.write(StateChangedEvent);
} }
for entity in &screens { for entity in &screens {