Skip to content
Snippets Groups Projects
query.rs 6.65 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;
Louis's avatar
Louis committed
use bevy::ecs::query::{QueryData, QueryFilter};
use bevy::prelude::{
	Commands, Entity, EventWriter, Handle, Parent, Query, Res, Time, With, Without,
};
Louis's avatar
Louis committed
use bevy::sprite::TextureAtlas;
Louis's avatar
Louis committed
#[derive(QueryData)]
#[query_data(mutable)]
pub struct AnimationComponents {
	handle: &'static Handle<AnimationSet>,
	status: &'static mut AnimationStatus,
Louis's avatar
Louis committed
	atlas: &'static mut TextureAtlas,
Louis's avatar
Louis committed
#[derive(QueryData)]
#[query_data(mutable)]
pub struct DirectionalAnimationComponents {
	handle: &'static Handle<AnimationSet>,
	direction: &'static Directionality,
	status: &'static mut AnimationStatus,
Louis's avatar
Louis committed
	atlas: &'static mut TextureAtlas,
Louis's avatar
Louis committed
#[derive(QueryData)]
#[query_data(mutable)]
pub struct OverrideAnimationComponents {
	handle: &'static Handle<AnimationSet>,
	data: Option<&'static OverrideData>,
	status: &'static mut AnimationOverride,
Louis's avatar
Louis committed
	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,
}

Louis's avatar
Louis committed
#[derive(QueryData)]
#[query_data(mutable)]
pub struct SimpleAnimationComponents {
	anim: &'static SimpleAnimation,
	status: &'static mut SimpleAnimationStatus,
Louis's avatar
Louis committed
	atlas: &'static mut TextureAtlas,
Louis's avatar
Louis committed
#[derive(QueryFilter)]
pub struct OnlyAnimations {
	_status: With<AnimationStatus>,
	_override: Without<AnimationOverride>,
	_direction: Without<Directionality>,
	_paused: Without<AnimationPaused>,
}
Louis's avatar
Louis committed
#[derive(QueryFilter)]
pub struct OnlyDirectionalAnimations {
	_status: With<AnimationStatus>,
	_direction: With<Directionality>,
	_override: Without<AnimationOverride>,
	_paused: Without<AnimationPaused>,
}
Louis's avatar
Louis committed
#[derive(QueryFilter)]
pub struct OnlyOverrideAnimations {
	_override: With<AnimationOverride>,
	_direction: Without<Directionality>,
	_paused: Without<AnimationPaused>,
}
Louis's avatar
Louis committed
#[derive(QueryFilter)]
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
	($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] {
Louis's avatar
Louis committed
			$atlas.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,
Louis's avatar
Louis committed
		mut atlas,
	} in &mut anim_query
	{
		let anim = get_current_anim!(animations, handle, status.active_name);
Louis's avatar
Louis committed
		tick_animation!(delta, anim, status, atlas);
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,
Louis's avatar
Louis committed
		mut atlas,
		direction,
	} in &mut anim_query
	{
		let anim = get_current_anim!(
			animations,
			handle,
			format!("{}_{}", status.active_name, direction),
			status.active_name
		);

Louis's avatar
Louis committed
		tick_animation!(delta, anim, status, atlas);
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,
Louis's avatar
Louis committed
			mut atlas,
			data,
		},
	) in &mut anim_query
	{
		let anim = get_current_anim!(animations, handle, status.active_name);
Louis's avatar
Louis committed
		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,
Louis's avatar
Louis committed
		mut atlas,
Louis's avatar
Louis committed
		tick_animation!(delta, *anim, status, atlas);
Louis's avatar
Louis committed
	}
Louis's avatar
Louis committed
	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;