diff --git a/src/animation.rs b/src/animation.rs new file mode 100644 index 0000000..736f964 --- /dev/null +++ b/src/animation.rs @@ -0,0 +1,178 @@ +use std::{f32::consts::PI, ops::RangeInclusive}; + +use bevy::{ + animation::{animated_field, AnimationTarget, AnimationTargetId}, + prelude::*, + utils::HashMap, +}; + +use crate::{ + deck::Deck, + play::{self, Selected}, +}; + +pub struct AnimationPlugin; + +impl Plugin for AnimationPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Update, delayed_animation); + } +} + +#[derive(Event, Clone)] +struct AnimationComplete; + +#[derive(Resource, Default)] +pub(crate) struct AnimationStore { + pub store: HashMap, + pub graph: AnimationGraphHandle, +} + +#[derive(Component)] +pub(crate) struct DelayedAnimation { + pub graph: AnimationGraphHandle, + pub animation_index: AnimationNodeIndex, + pub delay: Timer, +} + +pub(crate) fn setup_animations( + mut clips: ResMut>, + mut graphs: ResMut>, + mut commands: Commands, +) { + let mut animation_store = AnimationStore::default(); + + let mut animation_graph = AnimationGraph::new(); + + let targets: Vec = Deck::iter_cards() + .map(|c| Name::new(format!("{}", c))) + .map(|n| AnimationTargetId::from_name(&n)) + .collect(); + + // Rotation Animation + { + let mut animation = AnimationClip::default(); + targets.iter().for_each(|target| { + let curve = AnimatableCurve::new( + animated_field!(Transform::rotation), + AnimatableKeyframeCurve::new([0.0, 5.0, 10.0, 15.0, 20.0].into_iter().zip([ + Quat::IDENTITY, + Quat::from_axis_angle(Vec3::Z, PI / 2.), + Quat::from_axis_angle(Vec3::Z, PI / 2. * 2.), + Quat::from_axis_angle(Vec3::Z, PI / 2. * 3.), + Quat::IDENTITY, + ])) + .expect("Rotation animation"), + ); + animation.add_curve_to_target(*target, curve); + }); + let animation_handle = clips.add(animation); + let animation_index = animation_graph.add_clip(animation_handle, 1.0, animation_graph.root); + animation_store + .store + .insert("rotate".into(), animation_index); + } + + { + // For each spot on board + RangeInclusive::::new(0, 3).for_each(|x| { + RangeInclusive::::new(0, 3).for_each(|y| { + let a = Vec3::new(-400.0, -200.0, 0.0); + let b = play::card_placement(&play::PlayLocation { x, y }); + let c = Vec3::new(400.0, -200.0, 0.0); + + // Serve Deck -> Spot Animation + { + let mut animation = AnimationClip::default(); + targets.iter().for_each(|target| { + let curve = AnimatableCurve::new( + animated_field!(Transform::translation), + AnimatableKeyframeCurve::new([0.0, 1.0].into_iter().zip([a, b])) + .expect("Serve Card animation"), + ); + animation.add_curve_to_target(*target, curve); + }); + animation.set_duration(1.0); + animation.add_event(1.0, AnimationComplete); + let animation_handle = clips.add(animation); + let animation_index = + animation_graph.add_clip(animation_handle, 1.0, animation_graph.root); + animation_store + .store + .insert(format!("deck->{:?}", (x, y)), animation_index); + } + + // Spot -> Discard Animation + { + let mut animation = AnimationClip::default(); + targets.iter().for_each(|target| { + let curve = AnimatableCurve::new( + animated_field!(Transform::translation), + AnimatableKeyframeCurve::new([0.0, 1.0].into_iter().zip([b, c])) + .expect("Serve Card animation"), + ); + animation.add_curve_to_target(*target, curve); + }); + animation.set_duration(1.0); + animation.add_event(1.0, AnimationComplete); + let animation_handle = clips.add(animation); + let animation_index = + animation_graph.add_clip(animation_handle, 1.0, animation_graph.root); + animation_store + .store + .insert(format!("{:?}->discard", (x, y)), animation_index); + } + }); + }); + } + + animation_store.graph = AnimationGraphHandle(graphs.add(animation_graph)); + + commands.insert_resource(animation_store); + + // TODO: (example: https://bevyengine.org/examples/animation/animated-transform/) + // Button Animations: + // active_button_animation = AnimationClip::default() // color and size +} + +pub(crate) fn play_selected_animation( + trigger: Trigger, + mut query: Query<&mut AnimationPlayer>, + store: Res, +) { + let ai = store.store.get("rotate".into()).unwrap(); + query + .get_mut(trigger.entity()) + .unwrap() + .play(ai.clone()) + .repeat(); +} + +pub(crate) fn stop_selected_animation( + trigger: Trigger, + mut query: Query<(&mut Transform, &mut AnimationPlayer)>, +) { + let (mut t, mut ap) = query.get_mut(trigger.entity()).unwrap(); + ap.stop_all(); + t.rotation = Quat::default(); +} + +fn delayed_animation( + mut query: Query<(Entity, &mut DelayedAnimation, &mut AnimationPlayer), With>, + mut commands: Commands, + time: Res