//! Card flip animation and drag shadows. use super::*; use std::collections::HashSet; use solitaire_core::Card; use crate::animation_plugin::EffectiveSlideDuration; use crate::events::{CardFaceRevealedEvent, CardFlippedEvent}; use crate::layout::LayoutResource; use crate::resources::DragState; use crate::ui_theme::{CARD_SHADOW_ALPHA_DRAG, CARD_SHADOW_COLOR, CARD_SHADOW_LOCAL_Z}; /// Listens for `CardFlippedEvent` and inserts a `CardFlipAnim` on the entity. /// /// Skipped when `EffectiveSlideDuration::slide_secs == 0.0` (Instant speed). pub(super) fn start_flip_anim( mut events: MessageReader, slide_dur: Option>, mut commands: Commands, card_entities: Query<(Entity, &CardEntity)>, ) { if slide_dur.is_some_and(|d| d.slide_secs == 0.0) { // Instant animation speed — skip the flip effect entirely. events.clear(); return; } for CardFlippedEvent(flipped_card) in events.read() { for (entity, marker) in &card_entities { if marker.card == *flipped_card { commands.entity(entity).insert(CardFlipAnim { timer: 0.0, phase: FlipPhase::ScalingDown, }); break; } } } } /// Advances `CardFlipAnim` each frame, modifying `Transform::scale.x`. /// /// - Phase `ScalingDown`: lerps scale.x from 1.0 → 0.0 over `FLIP_HALF_SECS`. /// - At the midpoint the phase switches to `ScalingUp`, scale.x resets to 0, /// and a `CardFaceRevealedEvent` is fired so audio plays in sync with the reveal. /// - Phase `ScalingUp`: lerps scale.x from 0.0 → 1.0 over `FLIP_HALF_SECS`. /// - When complete the component is removed and scale.x is restored to 1.0. pub(super) fn tick_flip_anim( mut commands: Commands, time: Res