feat(engine): card visual improvements — flip animation, foundation/tableau placeholders, drag shadow
Task #34: CardFlipAnim component + start_flip_anim/tick_flip_anim systems animate revealed cards by squashing scale.x to 0 then expanding back to 1 (2×0.08 s). Skipped at Instant speed. Task #35: spawn_pile_markers now adds a Text2d child (S/H/D/C, 45% alpha) on Foundation markers so the suit is visible while the pile is empty. Task #43: Tableau pile markers get a "K" Text2d child (35% alpha) indicating only Kings land on empty columns. Task #38: update_drag_shadow system maintains a single ShadowEntity while dragging — a card_w+8 × card_h+8 dark semi-transparent sprite at z−1 behind the top dragged card. Also fixed pre-existing clippy/compiler errors in hud_plugin, pause_plugin, stats_plugin, cursor_plugin, and settings_plugin (missing imports, too-many-arguments, doc formatting). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -124,9 +124,22 @@ fn apply_theme_on_settings_change(
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the single-letter suit symbol used on empty foundation markers.
|
||||
///
|
||||
/// Matches the same ASCII convention used by `CardPlugin` for card labels.
|
||||
pub fn suit_symbol(suit: &Suit) -> &'static str {
|
||||
match suit {
|
||||
Suit::Spades => "S",
|
||||
Suit::Hearts => "H",
|
||||
Suit::Diamonds => "D",
|
||||
Suit::Clubs => "C",
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_pile_markers(commands: &mut Commands, layout: &Layout) {
|
||||
let marker_colour = Color::srgba(1.0, 1.0, 1.0, 0.08);
|
||||
let marker_size = layout.card_size;
|
||||
let font_size = layout.card_size.x * 0.28;
|
||||
|
||||
let mut piles: Vec<PileType> = Vec::with_capacity(13);
|
||||
piles.push(PileType::Stock);
|
||||
@@ -140,15 +153,40 @@ fn spawn_pile_markers(commands: &mut Commands, layout: &Layout) {
|
||||
|
||||
for pile in piles {
|
||||
let pos = layout.pile_positions[&pile];
|
||||
commands.spawn((
|
||||
let mut entity = commands.spawn((
|
||||
Sprite {
|
||||
color: marker_colour,
|
||||
custom_size: Some(marker_size),
|
||||
..default()
|
||||
},
|
||||
Transform::from_xyz(pos.x, pos.y, Z_PILE_MARKER),
|
||||
PileMarker(pile),
|
||||
PileMarker(pile.clone()),
|
||||
));
|
||||
|
||||
// Task #35 — suit symbol on empty foundation placeholders.
|
||||
if let PileType::Foundation(suit) = &pile {
|
||||
let symbol = suit_symbol(suit).to_string();
|
||||
entity.with_children(|b| {
|
||||
b.spawn((
|
||||
Text2d::new(symbol),
|
||||
TextFont { font_size, ..default() },
|
||||
TextColor(Color::srgba(1.0, 1.0, 1.0, 0.45)),
|
||||
Transform::from_xyz(0.0, 0.0, 0.1),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
// Task #43 — King indicator on empty tableau placeholders.
|
||||
if let PileType::Tableau(_) = &pile {
|
||||
entity.with_children(|b| {
|
||||
b.spawn((
|
||||
Text2d::new("K"),
|
||||
TextFont { font_size, ..default() },
|
||||
TextColor(Color::srgba(1.0, 1.0, 1.0, 0.35)),
|
||||
Transform::from_xyz(0.0, 0.0, 0.1),
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,4 +329,26 @@ mod tests {
|
||||
assert_eq!(c4, c5, "indices 4 and 5 must share the charcoal fallback");
|
||||
assert_eq!(c4, c99, "index 99 must share the charcoal fallback");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// suit_symbol pure-function tests (Task #35)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn suit_symbol_returns_correct_letters() {
|
||||
assert_eq!(suit_symbol(&Suit::Spades), "S");
|
||||
assert_eq!(suit_symbol(&Suit::Hearts), "H");
|
||||
assert_eq!(suit_symbol(&Suit::Diamonds), "D");
|
||||
assert_eq!(suit_symbol(&Suit::Clubs), "C");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suit_symbol_all_four_are_distinct() {
|
||||
let symbols: Vec<&str> = [Suit::Spades, Suit::Hearts, Suit::Diamonds, Suit::Clubs]
|
||||
.iter()
|
||||
.map(suit_symbol)
|
||||
.collect();
|
||||
let unique: std::collections::HashSet<&&str> = symbols.iter().collect();
|
||||
assert_eq!(unique.len(), 4, "all four suit symbols must be distinct");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user