use bevy::prelude::*;
use std::marker::PhantomData;
use crate::{guards::Guards, transitions::Source, StateChildOf};
#[derive(Component)]
pub struct FloatParam<P> {
value: f32,
_marker: PhantomData<P>,
}
impl<P> Default for FloatParam<P> {
fn default() -> Self { Self { value: 0.0, _marker: PhantomData } }
}
impl<P> FloatParam<P> {
#[inline]
pub fn get(&self) -> f32 { self.value }
#[inline]
pub fn set(&mut self, value: f32) { self.value = value; }
}
#[derive(Component)]
pub struct IntParam<P> {
value: i32,
_marker: PhantomData<P>,
}
impl<P> Default for IntParam<P> {
fn default() -> Self { Self { value: 0, _marker: PhantomData } }
}
impl<P> IntParam<P> {
#[inline]
pub fn get(&self) -> i32 { self.value }
#[inline]
pub fn set(&mut self, value: i32) { self.value = value; }
}
#[derive(Component)]
pub struct BoolParam<P> {
value: bool,
_marker: PhantomData<P>,
}
impl<P> Default for BoolParam<P> {
fn default() -> Self { Self { value: false, _marker: PhantomData } }
}
impl<P> BoolParam<P> {
#[inline]
pub fn get(&self) -> bool { self.value }
#[inline]
pub fn set(&mut self, value: bool) { self.value = value; }
}
pub trait FloatParamBinding<T: Component> {
fn extract(source: &T) -> f32;
}
pub fn sync_float_param<T, P>(
mut q: Query<(&T, &mut FloatParam<P>)>,
)
where
T: Component,
P: FloatParamBinding<T> + Send + Sync + 'static,
{
for (src, mut param) in &mut q {
param.set(P::extract(src));
}
}
#[derive(Component, Clone, Copy)]
pub struct FloatInRange<P> {
pub min: f32,
pub max: f32,
pub hysteresis: f32,
_marker: PhantomData<P>,
}
impl<P> FloatInRange<P> {
pub fn new(min: f32, max: f32, hysteresis: f32) -> Self {
Self { min, max, hysteresis, _marker: PhantomData }
}
}
fn guard_key_for_float<P>() -> String { format!("float-in-range::<{}>", std::any::type_name::<P>()) }
pub fn apply_float_param_guards<P: Send + Sync + 'static>(
q_edges: Query<(Entity, &Source, &FloatInRange<P>)>,
q_params: Query<&FloatParam<P>>,
q_child_of: Query<&StateChildOf>,
mut q_guards: Query<&mut Guards>,
mut commands: Commands,
) {
let key = guard_key_for_float::<P>();
for (edge, Source(source), range) in &q_edges {
let root = q_child_of.root_ancestor(*source);
let desired_blocked = match q_params.get(root) {
Ok(param) => {
let v = param.get();
!(v + range.hysteresis >= range.min && v - range.hysteresis <= range.max)
}
Err(_) => true, };
let current_has = q_guards
.get(edge)
.ok()
.map(|g| g.has_guard(key.as_str()))
.unwrap_or(false);
if desired_blocked != current_has {
if let Ok(mut g) = q_guards.get_mut(edge) {
if desired_blocked { g.add_guard(key.as_str()); }
else { g.remove_guard(key.as_str()); }
} else if desired_blocked {
commands.entity(edge).insert(Guards::init([key.as_str()]));
}
}
}
}
pub trait IntParamBinding<T: Component> {
fn extract(source: &T) -> i32;
}
pub fn sync_int_param<T, P>(
mut q: Query<(&T, &mut IntParam<P>)>,
)
where
T: Component,
P: IntParamBinding<T> + Send + Sync + 'static,
{
for (src, mut param) in &mut q {
param.set(P::extract(src));
}
}
#[derive(Component, Clone, Copy)]
pub struct IntInRange<P> {
pub min: i32,
pub max: i32,
pub hysteresis: i32,
_marker: PhantomData<P>,
}
impl<P> IntInRange<P> {
pub fn new(min: i32, max: i32, hysteresis: i32) -> Self {
Self { min, max, hysteresis, _marker: PhantomData }
}
}
fn guard_key_for_int<P>() -> String { format!("int-in-range::<{}>", std::any::type_name::<P>()) }
pub fn apply_int_param_guards<P: Send + Sync + 'static>(
q_edges: Query<(Entity, &Source, &IntInRange<P>)>,
q_params: Query<&IntParam<P>>,
q_child_of: Query<&StateChildOf>,
mut q_guards: Query<&mut Guards>,
mut commands: Commands,
){
let key = guard_key_for_int::<P>();
for (edge, Source(source), range) in &q_edges {
let root = q_child_of.root_ancestor(*source);
let desired_blocked = match q_params.get(root) {
Ok(param) => {
let v = param.get();
!((v + range.hysteresis) as i64 >= range.min as i64 && (v - range.hysteresis) as i64 <= range.max as i64)
}
Err(_) => true,
};
let current_has = q_guards
.get(edge)
.ok()
.map(|g| g.has_guard(key.as_str()))
.unwrap_or(false);
if desired_blocked != current_has {
if let Ok(mut g) = q_guards.get_mut(edge) {
if desired_blocked { g.add_guard(key.as_str()); }
else { g.remove_guard(key.as_str()); }
} else if desired_blocked {
commands.entity(edge).insert(Guards::init([key.as_str()]));
}
}
}
}
pub trait BoolParamBinding<T: Component> {
fn extract(source: &T) -> bool;
}
pub fn sync_bool_param<T, P>(
mut q: Query<(&T, &mut BoolParam<P>)>,
)
where
T: Component,
P: BoolParamBinding<T> + Send + Sync + 'static,
{
for (src, mut param) in &mut q {
param.set(P::extract(src));
}
}
#[derive(Component, Clone, Copy)]
pub struct BoolEquals<P> {
pub expected: bool,
_marker: PhantomData<P>,
}
impl<P> BoolEquals<P> {
pub fn new(expected: bool) -> Self { Self { expected, _marker: PhantomData } }
}
fn guard_key_for_bool<P>() -> String { format!("bool-equals::<{}>", std::any::type_name::<P>()) }
pub fn apply_bool_param_guards<P: Send + Sync + 'static>(
q_edges: Query<(Entity, &Source, &BoolEquals<P>)>,
q_params: Query<&BoolParam<P>>,
q_child_of: Query<&StateChildOf>,
mut q_guards: Query<&mut Guards>,
mut commands: Commands,
){
let key = guard_key_for_bool::<P>();
for (edge, Source(source), eq) in &q_edges {
let root = q_child_of.root_ancestor(*source);
let desired_blocked = match q_params.get(root) {
Ok(param) => param.get() != eq.expected,
Err(_) => true,
};
let current_has = q_guards
.get(edge)
.ok()
.map(|g| g.has_guard(key.as_str()))
.unwrap_or(false);
if desired_blocked != current_has {
if let Ok(mut g) = q_guards.get_mut(edge) {
if desired_blocked { g.add_guard(key.as_str()); }
else { g.remove_guard(key.as_str()); }
} else if desired_blocked {
commands.entity(edge).insert(Guards::init([key.as_str()]));
}
}
}
}