feat(engine): wire AnimSpeed to animation, new achievements, leaderboard opt-in, daily goal display
- AnimSpeed setting now drives card slide duration (Normal=0.15s, Fast=0.07s, Instant=snap); EffectiveSlideDuration resource updated on SettingsChangedEvent; AnimSpeed row added to Settings panel - GameState.recycle_count tracks waste recycles; perfectionist/comeback/zen_winner achievements added with full unit tests - SyncProvider gains opt_in_leaderboard(); SolitaireServerClient implements POST /api/leaderboard/opt-in; Opt In button added to leaderboard panel - DailyChallengeResource stores goal_description/target_score/max_time_secs from server; pressing C shows goal description as toast (DailyGoalAnnouncementEvent) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ use std::path::PathBuf;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use solitaire_core::game_state::DrawMode;
|
||||
use solitaire_data::{load_settings_from, save_settings_to, settings_file_path, settings::Theme, Settings};
|
||||
use solitaire_data::{load_settings_from, save_settings_to, settings_file_path, settings::Theme, AnimSpeed, Settings};
|
||||
|
||||
use crate::events::ManualSyncRequestEvent;
|
||||
use crate::progress_plugin::ProgressResource;
|
||||
@@ -66,6 +66,10 @@ struct SyncStatusText;
|
||||
#[derive(Component, Debug)]
|
||||
struct CardBackText;
|
||||
|
||||
/// Marks the `Text` node showing the current animation speed.
|
||||
#[derive(Component, Debug)]
|
||||
struct AnimSpeedText;
|
||||
|
||||
/// Marks the `Text` node showing the active background index.
|
||||
#[derive(Component, Debug)]
|
||||
struct BackgroundText;
|
||||
@@ -78,6 +82,7 @@ enum SettingsButton {
|
||||
MusicDown,
|
||||
MusicUp,
|
||||
ToggleDrawMode,
|
||||
CycleAnimSpeed,
|
||||
ToggleTheme,
|
||||
CycleCardBack,
|
||||
CycleBackground,
|
||||
@@ -135,6 +140,7 @@ impl Plugin for SettingsPlugin {
|
||||
update_sync_status_text,
|
||||
update_card_back_text,
|
||||
update_background_text,
|
||||
update_anim_speed_text,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -283,6 +289,18 @@ fn update_background_text(
|
||||
}
|
||||
}
|
||||
|
||||
fn update_anim_speed_text(
|
||||
settings: Res<SettingsResource>,
|
||||
mut text_nodes: Query<&mut Text, With<AnimSpeedText>>,
|
||||
) {
|
||||
if !settings.is_changed() {
|
||||
return;
|
||||
}
|
||||
for mut text in &mut text_nodes {
|
||||
**text = anim_speed_label(&settings.0.animation_speed);
|
||||
}
|
||||
}
|
||||
|
||||
fn card_back_label(idx: usize) -> String {
|
||||
if idx == 0 {
|
||||
"Default".to_string()
|
||||
@@ -328,10 +346,11 @@ fn handle_settings_buttons(
|
||||
mut changed: EventWriter<SettingsChangedEvent>,
|
||||
mut manual_sync: EventWriter<ManualSyncRequestEvent>,
|
||||
progress: Option<Res<ProgressResource>>,
|
||||
mut sfx_text: Query<&mut Text, (With<SfxVolumeText>, Without<MusicVolumeText>, Without<DrawModeText>, Without<ThemeText>)>,
|
||||
mut music_text: Query<&mut Text, (With<MusicVolumeText>, Without<SfxVolumeText>, Without<DrawModeText>, Without<ThemeText>)>,
|
||||
mut draw_text: Query<&mut Text, (With<DrawModeText>, Without<SfxVolumeText>, Without<MusicVolumeText>, Without<ThemeText>)>,
|
||||
mut theme_text: Query<&mut Text, (With<ThemeText>, Without<SfxVolumeText>, Without<MusicVolumeText>, Without<DrawModeText>)>,
|
||||
mut sfx_text: Query<&mut Text, (With<SfxVolumeText>, Without<MusicVolumeText>, Without<DrawModeText>, Without<ThemeText>, Without<AnimSpeedText>)>,
|
||||
mut music_text: Query<&mut Text, (With<MusicVolumeText>, Without<SfxVolumeText>, Without<DrawModeText>, Without<ThemeText>, Without<AnimSpeedText>)>,
|
||||
mut draw_text: Query<&mut Text, (With<DrawModeText>, Without<SfxVolumeText>, Without<MusicVolumeText>, Without<ThemeText>, Without<AnimSpeedText>)>,
|
||||
mut theme_text: Query<&mut Text, (With<ThemeText>, Without<SfxVolumeText>, Without<MusicVolumeText>, Without<DrawModeText>, Without<AnimSpeedText>)>,
|
||||
mut anim_speed_text: Query<&mut Text, (With<AnimSpeedText>, Without<SfxVolumeText>, Without<MusicVolumeText>, Without<DrawModeText>, Without<ThemeText>)>,
|
||||
) {
|
||||
for (interaction, button) in &interaction_query {
|
||||
if *interaction != Interaction::Pressed {
|
||||
@@ -393,6 +412,18 @@ fn handle_settings_buttons(
|
||||
**t = draw_mode_label(&settings.0.draw_mode);
|
||||
}
|
||||
}
|
||||
SettingsButton::CycleAnimSpeed => {
|
||||
settings.0.animation_speed = match settings.0.animation_speed {
|
||||
AnimSpeed::Normal => AnimSpeed::Fast,
|
||||
AnimSpeed::Fast => AnimSpeed::Instant,
|
||||
AnimSpeed::Instant => AnimSpeed::Normal,
|
||||
};
|
||||
persist(&path, &settings.0);
|
||||
changed.send(SettingsChangedEvent(settings.0.clone()));
|
||||
if let Ok(mut t) = anim_speed_text.get_single_mut() {
|
||||
**t = anim_speed_label(&settings.0.animation_speed);
|
||||
}
|
||||
}
|
||||
SettingsButton::ToggleTheme => {
|
||||
settings.0.theme = match settings.0.theme {
|
||||
Theme::Green => Theme::Blue,
|
||||
@@ -442,6 +473,14 @@ fn draw_mode_label(mode: &DrawMode) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn anim_speed_label(speed: &AnimSpeed) -> String {
|
||||
match speed {
|
||||
AnimSpeed::Normal => "Normal".into(),
|
||||
AnimSpeed::Fast => "Fast".into(),
|
||||
AnimSpeed::Instant => "Instant".into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn theme_label(theme: &Theme) -> String {
|
||||
match theme {
|
||||
Theme::Green => "Green".into(),
|
||||
@@ -538,6 +577,28 @@ fn spawn_settings_panel(
|
||||
icon_button(row, "⇄", SettingsButton::ToggleDrawMode);
|
||||
});
|
||||
|
||||
// Animation speed row
|
||||
card.spawn(Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
align_items: AlignItems::Center,
|
||||
column_gap: Val::Px(8.0),
|
||||
..default()
|
||||
})
|
||||
.with_children(|row| {
|
||||
row.spawn((
|
||||
Text::new("Anim Speed"),
|
||||
TextFont { font_size: 18.0, ..default() },
|
||||
TextColor(Color::srgb(0.85, 0.85, 0.80)),
|
||||
));
|
||||
row.spawn((
|
||||
AnimSpeedText,
|
||||
Text::new(anim_speed_label(&settings.animation_speed)),
|
||||
TextFont { font_size: 18.0, ..default() },
|
||||
TextColor(Color::WHITE),
|
||||
));
|
||||
icon_button(row, "⇄", SettingsButton::CycleAnimSpeed);
|
||||
});
|
||||
|
||||
// --- Appearance section ---
|
||||
section_label(card, "Appearance");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user