use crate::definitions::{ AnimationOverride, AnimationPaused, AnimationSet, AnimationStatus, OverrideData, SimpleAnimation, SimpleAnimationStatus, SyncToParent, }; use crate::directionality::Directionality; use crate::systems::AnimationCompleted; use bevy::asset::Assets; use bevy::ecs::query::{QueryData, QueryFilter}; use bevy::prelude::{ Commands, Entity, EventWriter, Handle, Parent, Query, Res, Time, With, Without, }; use bevy::sprite::TextureAtlas; #[derive(QueryData)] #[query_data(mutable)] pub struct AnimationComponents { handle: &'static Handle<AnimationSet>, status: &'static mut AnimationStatus, atlas: &'static mut TextureAtlas, } #[derive(QueryData)] #[query_data(mutable)] pub struct DirectionalAnimationComponents { handle: &'static Handle<AnimationSet>, direction: &'static Directionality, status: &'static mut AnimationStatus, atlas: &'static mut TextureAtlas, } #[derive(QueryData)] #[query_data(mutable)] pub struct OverrideAnimationComponents { handle: &'static Handle<AnimationSet>, data: Option<&'static OverrideData>, status: &'static mut AnimationOverride, atlas: &'static mut TextureAtlas, } #[derive(QueryData)] #[query_data(mutable)] pub struct DirectionalOverrideAnimationComponents { handle: &'static Handle<AnimationSet>, direction: &'static Directionality, data: Option<&'static OverrideData>, status: &'static mut AnimationOverride, atlas: &'static mut TextureAtlas, } #[derive(QueryData)] #[query_data(mutable)] pub struct SimpleAnimationComponents { anim: &'static SimpleAnimation, status: &'static mut SimpleAnimationStatus, atlas: &'static mut TextureAtlas, } #[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) { 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) { 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, $atlas: expr) => {{ 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 }}; } 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 atlas, } in &mut anim_query { let anim = get_current_anim!(animations, handle, status.active_name); tick_animation!(delta, anim, status, atlas); } } 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 atlas, 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, atlas); } } 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 atlas, data, }, ) in &mut anim_query { let anim = get_current_anim!(animations, handle, status.active_name); let looped = tick_animation!(delta, anim, status, atlas); if looped { commands .entity(entity) .remove::<(AnimationOverride, OverrideData)>(); if let Some(data) = data { events.send(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 atlas, 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, atlas); if looped { commands .entity(entity) .remove::<(AnimationOverride, OverrideData)>(); if let Some(data) = data { events.send(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 atlas, anim, } in &mut anim_query { tick_animation!(delta, *anim, status, atlas); } } pub fn sync_child_animation( mut children: Query<(&Parent, &mut TextureAtlas), With<SyncToParent>>, parents: Query<&TextureAtlas, Without<SyncToParent>>, ) { for (parent, mut child_sprite) in &mut children { if let Ok(parent_sprite) = parents.get(**parent) { if parent_sprite.index != child_sprite.index { child_sprite.index = parent_sprite.index; } } } }