feat(engine): grant achievement rewards + gate cosmetic selectors

- Add Reward enum to solitaire_core with CardBack/Background/BonusXp/Badge variants
- Wire rewards into ALL_ACHIEVEMENTS per architecture spec
- evaluate_on_win now applies rewards on first unlock: pushes cosmetic
  indices into PlayerProgress, awards BonusXp (with level-up detection),
  and marks reward_granted = true so rewards are never double-granted
- Add selected_card_back / selected_background fields to Settings
- Settings panel grows Card Back and Background cycle rows, shown only
  when the player has unlocked more than the default (index 0)
- cycle_unlocked() cycles only through earned options

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-04-27 01:00:18 +00:00
parent b37fe5b49b
commit 6728a4311f
4 changed files with 250 additions and 8 deletions
+12
View File
@@ -84,6 +84,14 @@ pub struct Settings {
/// Which sync backend is active.
#[serde(default)]
pub sync_backend: SyncBackend,
/// Index of the card-back design currently in use (0 = default).
/// Only indices present in `PlayerProgress::unlocked_card_backs` are valid.
#[serde(default)]
pub selected_card_back: usize,
/// Index of the background design currently in use (0 = default).
/// Only indices present in `PlayerProgress::unlocked_backgrounds` are valid.
#[serde(default)]
pub selected_background: usize,
/// Set to `true` once the player has dismissed the first-run banner.
#[serde(default)]
pub first_run_complete: bool,
@@ -110,6 +118,8 @@ impl Default for Settings {
animation_speed: AnimSpeed::Normal,
theme: Theme::Green,
sync_backend: SyncBackend::Local,
selected_card_back: 0,
selected_background: 0,
first_run_complete: false,
}
}
@@ -264,6 +274,8 @@ mod tests {
url: "https://example.com".to_string(),
username: "testuser".to_string(),
},
selected_card_back: 0,
selected_background: 0,
first_run_complete: true,
};
save_settings_to(&path, &s).expect("save");