You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
9.9 KiB
Rust

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<String, (AnimationGraphHandle, AnimationNodeIndex)>,
}
/// Setup drawing our cards on the screen
pub(crate) fn setup_cards(
mut commands: Commands,
deck: Res<Deck>,
mut layouts: ResMut<Assets<TextureAtlasLayout>>,
server: Res<AssetServer>,
) {
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<Assets<AnimationClip>>,
mut graphs: ResMut<Assets<AnimationGraph>>,
mut commands: Commands,
) {
let mut animation_store = AnimationStore::default();
let targets: Vec<AnimationTargetId> = 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::<u8>::new(0, 3).for_each(|x| {
RangeInclusive::<u8>::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<AssetServer>,
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<NextState<GameState>>,
mut view_state: ResMut<NextState<ViewState>>,
) {
info!("starting play");
game_state.set(GameState::Main);
view_state.set(ViewState::Menu);
}