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
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);
|
|
}
|