#![allow(missing_docs)]
use crate::{
charges::ChargeState,
cooldown::CooldownState,
pool::{AbilityCosts, MaxPoolLessThanMin, Pool},
Abilitylike, CannotUseAbility,
};
use bevy::{ecs::component::Component, ecs::query::QueryData};
use leafwing_input_manager::action_state::ActionState;
#[derive(QueryData)]
#[query_data(mutable)]
pub struct AbilityState<A: Abilitylike, P: Pool = NullPool> {
pub action_state: &'static ActionState<A>,
pub charges: &'static mut ChargeState<A>,
pub cooldowns: &'static mut CooldownState<A>,
pub pool: Option<&'static mut P>,
pub ability_costs: Option<&'static mut AbilityCosts<A, P>>,
}
impl<A: Abilitylike, P: Pool> AbilityStateItem<'_, '_, A, P> {
#[inline]
pub fn ready(&self, action: &A) -> Result<(), CannotUseAbility> {
let maybe_pool = self.pool.as_deref();
let maybe_ability_costs = self.ability_costs.as_deref();
action.ready(
&*self.charges,
&*self.cooldowns,
maybe_pool,
maybe_ability_costs,
)
}
#[inline]
pub fn ready_and_pressed(&self, action: &A) -> Result<(), CannotUseAbility> {
if self.action_state.pressed(action) {
self.ready(action)?;
Ok(())
} else {
Err(CannotUseAbility::NotPressed)
}
}
#[inline]
pub fn ready_and_just_pressed(&self, action: &A) -> Result<(), CannotUseAbility> {
if self.action_state.just_pressed(action) {
self.ready(action)?;
Ok(())
} else {
Err(CannotUseAbility::NotPressed)
}
}
#[inline]
pub fn trigger(&mut self, action: &A) -> Result<(), CannotUseAbility> {
let maybe_pool = self.pool.as_deref_mut();
let maybe_ability_costs = self.ability_costs.as_deref();
action.trigger(
&mut *self.charges,
&mut *self.cooldowns,
maybe_pool,
maybe_ability_costs,
)
}
#[inline]
pub fn trigger_if_pressed(&mut self, action: &A) -> Result<(), CannotUseAbility> {
if self.action_state.just_pressed(action) {
let maybe_pool = self.pool.as_deref_mut();
let maybe_ability_costs = self.ability_costs.as_deref();
action.trigger(
&mut *self.charges,
&mut *self.cooldowns,
maybe_pool,
maybe_ability_costs,
)
} else {
Err(CannotUseAbility::NotPressed)
}
}
#[inline]
pub fn trigger_if_just_pressed(&mut self, action: &A) -> Result<(), CannotUseAbility> {
if self.action_state.just_pressed(action) {
let maybe_pool = self.pool.as_deref_mut();
let maybe_ability_costs = self.ability_costs.as_deref();
action.trigger(
&mut *self.charges,
&mut *self.cooldowns,
maybe_pool,
maybe_ability_costs,
)
} else {
Err(CannotUseAbility::NotPressed)
}
}
}
impl<A: Abilitylike, P: Pool> AbilityStateReadOnlyItem<'_, '_, A, P> {
#[inline]
pub fn ready(&self, action: &A) -> Result<(), CannotUseAbility> {
action.ready(self.charges, self.cooldowns, self.pool, self.ability_costs)
}
#[inline]
pub fn ready_and_pressed(&self, action: &A) -> Result<(), CannotUseAbility> {
if self.action_state.pressed(action) {
self.ready(action)?;
Ok(())
} else {
Err(CannotUseAbility::NotPressed)
}
}
#[inline]
pub fn ready_and_just_pressed(&self, action: &A) -> Result<(), CannotUseAbility> {
if self.action_state.just_pressed(action) {
self.ready(action)?;
Ok(())
} else {
Err(CannotUseAbility::NotPressed)
}
}
}
#[cfg(test)]
mod tests {
use crate as leafwing_abilities;
use crate::{AbilitiesBundle, AbilityState, Abilitylike};
use bevy::{prelude::*, reflect::Reflect};
use leafwing_input_manager::{action_state::ActionState, Actionlike};
#[derive(Actionlike, Reflect, Abilitylike, Clone, Debug, Hash, PartialEq, Eq)]
enum TestAction {
Duck,
Cover,
}
#[test]
fn ability_state_methods_are_visible_from_query() {
fn simple_system(mut query: Query<AbilityState<TestAction>>) {
let mut ability_state = query.single_mut().unwrap();
let _triggered = ability_state.trigger(&TestAction::Duck);
}
let mut app = App::new();
app.add_systems(Update, simple_system);
}
#[test]
fn ability_state_fetches_abilities_bundle() {
let mut world = World::new();
world
.spawn(AbilitiesBundle::<TestAction>::default())
.insert(ActionState::<TestAction>::default());
let mut query_state = world.query::<AbilityState<TestAction>>();
assert_eq!(query_state.iter(&world).len(), 1);
}
}
#[derive(Component, Debug, Default)]
pub struct NullPool;
impl Pool for NullPool {
type Quantity = f32;
const MIN: f32 = 0.0;
fn current(&self) -> Self::Quantity {
Self::MIN
}
fn set_current(&mut self, _new_quantity: Self::Quantity) -> Self::Quantity {
Self::MIN
}
fn max(&self) -> Self::Quantity {
Self::MIN
}
fn set_max(&mut self, _new_max: Self::Quantity) -> Result<(), MaxPoolLessThanMin> {
Ok(())
}
}