use crate::definitions::{ AnimationHandle, AnimationOverride, AnimationPaused, AnimationSet, AnimationStatus, OverrideData, SimpleAnimation, SimpleAnimationStatus, SyncToParent, }; use crate::directionality::Directionality; use crate::systems::AnimationCompleted; use bevy_asset::Assets; use bevy_ecs::entity::Entity; use bevy_ecs::event::EventWriter; use bevy_ecs::hierarchy::ChildOf; use bevy_ecs::query::{QueryData, QueryFilter, With, Without}; use bevy_ecs::system::{Commands, Query, Res}; use bevy_sprite::Sprite; use bevy_time::Time; #[derive(QueryData)] #[query_data(mutable)] pub struct AnimationComponents { handle: &'static AnimationHandle, status: &'static mut AnimationStatus, sprite: &'static mut Sprite, } #[derive(QueryData)] #[query_data(mutable)] pub struct DirectionalAnimationComponents { handle: &'static AnimationHandle, direction: &'static Directionality, status: &'static mut AnimationStatus, sprite: &'static mut Sprite, } #[derive(QueryData)] #[query_data(mutable)] pub struct OverrideAnimationComponents { handle: &'static AnimationHandle, data: Option<&'static OverrideData>, status: &'static mut AnimationOverride, sprite: &'static mut Sprite, } #[derive(QueryData)] #[query_data(mutable)] pub struct DirectionalOverrideAnimationComponents { handle: &'static AnimationHandle, direction: &'static Directionality, data: Option<&'static OverrideData>, status: &'static mut AnimationOverride, sprite: &'static mut Sprite, } #[derive(QueryData)] #[query_data(mutable)] pub struct SimpleAnimationComponents { anim: &'static SimpleAnimation, status: &'static mut SimpleAnimationStatus, sprite: &'static mut Sprite, } #[derive(QueryFilter)] pub struct OnlyAnimations { _status: With<AnimationStatus>, _override: Without<AnimationOverride>, _direction: Without<Directionality>, _paused: Without<AnimationPaused>, } #[derive(QueryFilter)] pub struct OnlyDirectionalAnimations { _status: With<AnimationStatus>, _direction: With<Directionality>, _override: Without<AnimationOverride>, _paused: Without<AnimationPaused>, } #[derive(QueryFilter)] pub struct OnlyOverrideAnimations { _override: With<AnimationOverride>, _direction: Without<Directionality>, _paused: Without<AnimationPaused>, } #[derive(QueryFilter)] pub struct OnlyDirectionalOverrideAnimations { _override: With<AnimationOverride>, _direction: With<Directionality>, _paused: Without<AnimationPaused>, } macro_rules! get_current_anim { ($anims: expr, $handle: expr, $name: expr) => { match $anims.get($handle.id()) { Some(active) => match active.get(&$name) { Some(inner) => inner, None => continue, }, None => continue, } }; ($anims: expr, $handle: expr, $name: expr, $($also: expr),+) => { match $anims.get($handle.id()) { Some(active) => match active.get(&$name)$(.or_else(|| active.get(&$also)))+ { Some(inner) => inner, None => continue, }, None => continue, } }; } macro_rules! tick_animation { ($delta: expr, $anim: expr, $status: expr, $sprite: expr) => {{ if let Some(atlas) = $sprite.texture_atlas.as_mut() { let current_frame = atlas.index; let mut has_looped = false; $status.frame_time += $delta.as_secs_f32(); while $status.frame_time >= $anim.frame_secs { $status.frame_time = ($status.frame_time - $anim.frame_secs).max(0.0); let next_frame = $status.active_step.saturating_add(1); $status.active_step = if next_frame >= $anim.frames.len() { has_looped = true; 0 } else { next_frame }; } if current_frame != $anim.frames[$status.active_step] { atlas.index = $anim.frames[$status.active_step]; } has_looped } else { false } }}; } pub fn play_animations( time: Res<Time>, mut anim_query: Query<AnimationComponents, OnlyAnimations>, animations: Res<Assets<AnimationSet>>, ) { let delta = time.delta(); for AnimationComponentsItem { mut status, handle, mut sprite, } in &mut anim_query { let anim = get_current_anim!(animations, handle, status.active_name); tick_animation!(delta, anim, status, sprite); } } pub fn play_directional_animations( time: Res<Time>, mut anim_query: Query<DirectionalAnimationComponents, OnlyDirectionalAnimations>, animations: Res<Assets<AnimationSet>>, ) { let delta = time.delta(); for DirectionalAnimationComponentsItem { mut status, handle, mut sprite, direction, } in &mut anim_query { let anim = get_current_anim!( animations, handle, format!("{}_{}", status.active_name, direction), status.active_name ); tick_animation!(delta, anim, status, sprite); } } pub fn play_override_animation( time: Res<Time>, mut commands: Commands, mut anim_query: Query<(Entity, OverrideAnimationComponents), OnlyOverrideAnimations>, animations: Res<Assets<AnimationSet>>, mut events: EventWriter<AnimationCompleted>, ) { let delta = time.delta(); for ( entity, OverrideAnimationComponentsItem { mut status, handle, mut sprite, data, }, ) in &mut anim_query { let anim = get_current_anim!(animations, handle, status.active_name); let looped = tick_animation!(delta, anim, status, sprite); if looped { commands .entity(entity) .remove::<(AnimationOverride, OverrideData)>(); if let Some(data) = data { events.write(AnimationCompleted { entity, user_data: **data, }); } } } } pub fn play_directional_override_animation( time: Res<Time>, mut commands: Commands, mut anim_query: Query< (Entity, DirectionalOverrideAnimationComponents), OnlyDirectionalOverrideAnimations, >, animations: Res<Assets<AnimationSet>>, mut events: EventWriter<AnimationCompleted>, ) { let delta = time.delta(); for ( entity, DirectionalOverrideAnimationComponentsItem { mut status, direction, handle, mut sprite, data, }, ) in &mut anim_query { let anim = get_current_anim!( animations, handle, format!("{}_{}", status.active_name, direction), status.active_name ); let looped = tick_animation!(delta, anim, status, sprite); if looped { commands .entity(entity) .remove::<(AnimationOverride, OverrideData)>(); if let Some(data) = data { events.write(AnimationCompleted { entity, user_data: **data, }); } } } } pub fn play_simple_animation( time: Res<Time>, mut anim_query: Query<SimpleAnimationComponents, Without<AnimationPaused>>, ) { let delta = time.delta(); for SimpleAnimationComponentsItem { mut status, mut sprite, anim, } in &mut anim_query { tick_animation!(delta, *anim, status, sprite); } } pub fn sync_child_animation( mut children: Query<(&ChildOf, &mut Sprite), With<SyncToParent>>, parents: Query<&Sprite, Without<SyncToParent>>, ) { for (parent, mut child_sprite) in &mut children { if let Ok(parent_sprite) = parents.get(parent.parent()) { match ( parent_sprite.texture_atlas.as_ref(), child_sprite.texture_atlas.as_mut(), ) { (Some(parent_value), Some(child_value)) => { if child_value.index != parent_value.index { *child_value = parent_value.clone(); } } (Some(parent_value), None) => { child_sprite.texture_atlas = Some(parent_value.clone()); } _ => {} } } } }