use std::{f32::consts::PI, ops::RangeInclusive}; use crate::{deck::*, *}; use bevy::{ animation::{animated_field, AnimationTarget, AnimationTargetId}, prelude::*, utils::HashMap, }; use play::AnimationComplete; use view::ViewState; pub struct SetupPlugin; impl Plugin for SetupPlugin { fn build(&self, app: &mut App) { app.add_systems( OnEnter(GameState::Setup), ( setup_background, setup_cards, setup_camera, start_play, setup_animations, ) .chain(), ); } } #[derive(Resource, Default)] pub(crate) struct AnimationStore { pub store: HashMap, } /// Setup drawing our cards on the screen pub(crate) fn setup_cards( mut commands: Commands, deck: Res, mut layouts: ResMut>, server: Res, ) { let animation_player = AnimationPlayer::default(); commands .spawn(( Transform::default(), Visibility::default(), ViewState::Play, animation_player, )) .with_children(|parent| { // Top of card pile is a "face down" card { let top_card_transform = Transform { translation: Vec3::new(-400.0, -200.0, 1.0), ..default() }; let top_card_sprite = Sprite { custom_size: Some(Vec2::new(80.0, 128.0)), texture_atlas: Some(TextureAtlas { index: 108, layout: layouts.add(TextureAtlasLayout::from_grid( UVec2 { x: 20, y: 32 }, 9, 13, None, None, )), }), image: server.load("cards.png"), ..default() }; parent.spawn((top_card_transform, top_card_sprite, Visibility::Inherited)); } Deck::iter_shuffled() .enumerate() .for_each(|(i, this_card)| { let this = deck .cards .get(&this_card) .unwrap_or_else(|| panic!("fech card sprite {:?}", this_card)) .clone(); let this_sprite = Sprite { custom_size: Some(Vec2::new(80.0, 128.0)), ..this.clone() }; let order = play::DeckOrder(i as u8); let animation_player = AnimationPlayer::default(); let name = Name::new(format!("{}", this_card)); let animation_target_id = AnimationTargetId::from_name(&name); let this_transform = Transform::default().with_translation(Vec3::new(-400.0, -200.0, 0.0)); let visibility = Visibility::Hidden; // Spawn card with a simple Transform parent so we can adjust the Z-axis for // card ordering parent .spawn((Transform::default(), Visibility::Inherited)) .with_children(|parent| { let mut child = parent.spawn_empty(); let entity_id = child.id(); let animation_target = AnimationTarget { id: animation_target_id, player: entity_id, }; child .insert(( animation_player, animation_target, name, order, this_card, this_sprite, this_transform, visibility, )) .observe(play::place_card) .observe(debug::set_debug_card) .observe(debug::hide_debug_card) .observe(play::play_selected_animation) .observe(play::stop_selected_animation) .observe(play::toggle_selected); }); }); }); } fn setup_animations( mut clips: ResMut>, mut graphs: ResMut>, mut commands: Commands, ) { let mut animation_store = AnimationStore::default(); 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 (graph, animation_index) = AnimationGraph::from_clip(animation_handle); let graph_handle = AnimationGraphHandle(graphs.add(graph)); animation_store .store .insert("rotate".into(), (graph_handle, 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 (graph, animation_index) = AnimationGraph::from_clip(animation_handle); let graph_handle = AnimationGraphHandle(graphs.add(graph)); animation_store.store.insert( format!("deck->{:?}", (x, y)), (graph_handle, 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 (graph, animation_index) = AnimationGraph::from_clip(animation_handle); let graph_handle = AnimationGraphHandle(graphs.add(graph)); animation_store.store.insert( format!("{:?}->discard", (x, y)), (graph_handle, animation_index), ); } }); }); } commands.insert_resource(animation_store); // TODO: (example: https://bevyengine.org/examples/animation/animated-transform/) // Button Animations: // active_button_animation = AnimationClip::default() // color and size } /// Setup our camera to view cardson the screen pub(crate) fn setup_camera(mut commands: Commands) { commands.spawn(( Camera2d, Camera { hdr: true, ..default() }, Transform::default().with_translation(Vec3::new(0.0, 0.0, 100.0)), )); } pub(crate) fn setup_background( mut commands: Commands, server: Res, window: Query<&Window>, ) { commands.spawn(( Transform::default().with_translation(Vec3::new(0.0, 0.0, -1.0)), Sprite { image: server.load("background.png"), custom_size: Some(window.single().resolution.size() * 1.1), ..default() }, )); } /// Finish the setup state by progressing to the play state pub(crate) fn start_play( mut game_state: ResMut>, mut view_state: ResMut>, ) { info!("starting play"); game_state.set(GameState::Main); view_state.set(ViewState::Menu); }