refactor: remove leftover redundancies after card_game migration
Post-migration audit found the card_game/klondike migration essentially complete; these are the four small redundancies that remained: - core: delete dead GameState::compute_time_bonus (zero callers; engine uses the klondike_adapter free fn directly) - data: drop dead public re-exports load_latest_replay_from / save_latest_replay_to (no callers outside replay.rs); keep latest_replay_path (engine legacy migration still uses it) - data+engine: lift win-XP scoring into a shared XpBreakdown so the win-summary modal breakdown and xp_for_win share one source of truth instead of duplicating the speed/no-undo constants - engine: replace feedback_anim_plugin's private foundation_from_slot copy with the canonical klondike_adapter::foundation_from_slot cargo test --workspace + clippy -D warnings green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,8 @@ use std::hash::{Hash, Hasher};
|
||||
use bevy::prelude::*;
|
||||
use bevy::window::RequestRedraw;
|
||||
use solitaire_core::card::Card;
|
||||
use solitaire_core::{Foundation, KlondikePile};
|
||||
use solitaire_core::KlondikePile;
|
||||
use solitaire_core::klondike_adapter::foundation_from_slot;
|
||||
use solitaire_data::AnimSpeed;
|
||||
|
||||
use crate::animation_plugin::CardAnim;
|
||||
@@ -645,16 +646,6 @@ fn pile_cards(
|
||||
}
|
||||
}
|
||||
|
||||
fn foundation_from_slot(slot: u8) -> Option<Foundation> {
|
||||
match slot {
|
||||
0 => Some(Foundation::Foundation1),
|
||||
1 => Some(Foundation::Foundation2),
|
||||
2 => Some(Foundation::Foundation3),
|
||||
3 => Some(Foundation::Foundation4),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Unit tests (pure functions only — no Bevy world required)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -90,28 +90,23 @@ pub struct WinSummaryPending {
|
||||
|
||||
/// Builds a human-readable XP breakdown string for the win modal.
|
||||
///
|
||||
/// Mirrors the logic in `solitaire_data::xp_for_win` so the breakdown always
|
||||
/// matches the total shown on the `XpAwardedEvent`.
|
||||
/// Reads the components from `solitaire_data::xp_breakdown` — the single source
|
||||
/// of truth shared with `xp_for_win` — so the breakdown can never drift from
|
||||
/// the total shown on the `XpAwardedEvent`.
|
||||
///
|
||||
/// Examples:
|
||||
/// - slow win, no undo → `"+50 base +25 no-undo"`
|
||||
/// - fast win, undo → `"+50 base +30 speed"`
|
||||
/// - fast win, no undo → `"+50 base +25 no-undo +30 speed"`
|
||||
fn build_xp_detail(time_seconds: u64, used_undo: bool) -> String {
|
||||
let speed_bonus: u64 = if time_seconds >= 120 {
|
||||
0
|
||||
} else {
|
||||
let scaled = 50_u64.saturating_sub(time_seconds.saturating_mul(40) / 120);
|
||||
scaled.max(10)
|
||||
};
|
||||
let no_undo_bonus: u64 = if used_undo { 0 } else { 25 };
|
||||
let xp = solitaire_data::xp_breakdown(time_seconds, used_undo);
|
||||
|
||||
let mut parts = vec!["+50 base".to_string()];
|
||||
if no_undo_bonus > 0 {
|
||||
parts.push("+25 no-undo".to_string());
|
||||
let mut parts = vec![format!("+{} base", xp.base)];
|
||||
if xp.no_undo_bonus > 0 {
|
||||
parts.push(format!("+{} no-undo", xp.no_undo_bonus));
|
||||
}
|
||||
if speed_bonus > 0 {
|
||||
parts.push(format!("+{speed_bonus} speed"));
|
||||
if xp.speed_bonus > 0 {
|
||||
parts.push(format!("+{} speed", xp.speed_bonus));
|
||||
}
|
||||
parts.join(" ")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user