feat(data,engine): persistent Settings + SFX volume hotkeys

- solitaire_data::Settings { sfx_volume, first_run_complete } with
  atomic JSON persistence and clamping sanitizer.
- SettingsPlugin (engine): [ / ] adjust SFX volume by 0.1, clamped;
  persists on change; emits SettingsChangedEvent. No-op at rails.
- AudioPlugin applies sfx_volume to kira's main track at startup
  and on every change so live tweaks take effect without restart.
- Brief "SFX: N%" toast on each change. Help cheat sheet updated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-04-25 23:08:20 -07:00
parent b720588687
commit 9d0f9478b2
9 changed files with 389 additions and 7 deletions
+18
View File
@@ -13,6 +13,7 @@ use crate::events::{AchievementUnlockedEvent, GameWonEvent};
use crate::game_plugin::GameMutation;
use crate::layout::LayoutResource;
use crate::progress_plugin::LevelUpEvent;
use crate::settings_plugin::SettingsChangedEvent;
use crate::time_attack_plugin::TimeAttackEndedEvent;
use crate::weekly_goals_plugin::WeeklyGoalCompletedEvent;
@@ -26,6 +27,7 @@ const DAILY_TOAST_SECS: f32 = 3.0;
const WEEKLY_TOAST_SECS: f32 = 3.0;
const TIME_ATTACK_TOAST_SECS: f32 = 5.0;
const CHALLENGE_TOAST_SECS: f32 = 3.0;
const VOLUME_TOAST_SECS: f32 = 1.4;
const CASCADE_STAGGER: f32 = 0.05;
const CASCADE_DURATION: f32 = 0.5;
@@ -65,6 +67,7 @@ impl Plugin for AnimationPlugin {
.add_event::<WeeklyGoalCompletedEvent>()
.add_event::<TimeAttackEndedEvent>()
.add_event::<ChallengeAdvancedEvent>()
.add_event::<SettingsChangedEvent>()
.add_systems(
Update,
(
@@ -76,6 +79,7 @@ impl Plugin for AnimationPlugin {
handle_weekly_toast,
handle_time_attack_toast,
handle_challenge_toast,
handle_settings_toast,
tick_toasts,
)
.after(GameMutation),
@@ -215,6 +219,20 @@ fn handle_challenge_toast(
}
}
fn handle_settings_toast(
mut commands: Commands,
mut events: EventReader<SettingsChangedEvent>,
) {
for ev in events.read() {
let pct = (ev.0.sfx_volume * 100.0).round() as i32;
spawn_toast(
&mut commands,
format!("SFX: {pct}%"),
VOLUME_TOAST_SECS,
);
}
}
fn tick_toasts(
mut commands: Commands,
time: Res<Time>,