Skip to content
Snippets Groups Projects
query.rs 5.53 KiB
Newer Older
Louis's avatar
Louis committed
use crate::definitions::{
	AnimationOverride, AnimationPaused, AnimationSet, AnimationStatus, OverrideData,
	SimpleAnimation, SimpleAnimationStatus, SyncToParent,
Louis's avatar
Louis committed
};
use crate::directionality::Directionality;
Louis's avatar
Louis committed
use crate::systems::AnimationCompleted;
use bevy::asset::Assets;
use bevy::ecs::query::WorldQuery;
use bevy::prelude::{
	Commands, Entity, EventWriter, Handle, Parent, Query, Res, Time, With, Without,
};
use bevy::sprite::TextureAtlasSprite;

#[derive(WorldQuery)]
#[world_query(mutable)]
pub struct AnimationComponents {
	handle: &'static Handle<AnimationSet>,
	status: &'static mut AnimationStatus,
	sprite: &'static mut TextureAtlasSprite,
#[derive(WorldQuery)]
#[world_query(mutable)]
pub struct DirectionalAnimationComponents {
	handle: &'static Handle<AnimationSet>,
	direction: &'static Directionality,
	status: &'static mut AnimationStatus,
	sprite: &'static mut TextureAtlasSprite,
#[derive(WorldQuery)]
#[world_query(mutable)]
pub struct OverrideAnimationComponents {
	handle: &'static Handle<AnimationSet>,
	data: Option<&'static OverrideData>,
	status: &'static mut AnimationOverride,
	sprite: &'static mut TextureAtlasSprite,
#[derive(WorldQuery)]
#[world_query(mutable)]
pub struct SimpleAnimationComponents {
	anim: &'static SimpleAnimation,
	status: &'static mut SimpleAnimationStatus,
	sprite: &'static mut TextureAtlasSprite,
}
Louis's avatar
Louis committed

#[derive(WorldQuery)]
pub struct OnlyAnimations {
	_status: With<AnimationStatus>,
	_override: Without<AnimationOverride>,
	_direction: Without<Directionality>,
	_paused: Without<AnimationPaused>,
}
Louis's avatar
Louis committed

#[derive(WorldQuery)]
pub struct OnlyDirectionalAnimations {
	_status: With<AnimationStatus>,
	_direction: With<Directionality>,
	_override: Without<AnimationOverride>,
	_paused: Without<AnimationPaused>,
}
Louis's avatar
Louis committed

#[derive(WorldQuery)]
pub struct OnlyOverrideAnimations {
	_override: With<AnimationOverride>,
	_direction: Without<Directionality>,
	_paused: Without<AnimationPaused>,
}
Louis's avatar
Louis committed

#[derive(WorldQuery)]
pub struct OnlyDirectionalOverrideAnimations {
	_override: With<AnimationOverride>,
	_direction: With<Directionality>,
	_paused: Without<AnimationPaused>,
}
Louis's avatar
Louis committed

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,
Louis's avatar
Louis committed
		}
	};
	($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,
		}
	};
}
Louis's avatar
Louis committed

macro_rules! tick_animation {
	($delta: expr, $anim: expr, $status: expr, $sprite: expr) => {{
		let current_frame = $sprite.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] {
			$sprite.index = $anim.frames[$status.active_step];
		}
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);
Louis's avatar
Louis committed
	}
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);
Louis's avatar
Louis committed
	}
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.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 sprite,
		anim,
	} in &mut anim_query
	{
		tick_animation!(delta, *anim, status, sprite);
Louis's avatar
Louis committed
	}
pub fn sync_child_animation(
	mut children: Query<(&Parent, &mut TextureAtlasSprite), With<SyncToParent>>,
	parents: Query<&TextureAtlasSprite, 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;