use core::time::Duration;
use bevy::prelude::*;
use super::DEFAULT_ACTUATION;
use crate::prelude::*;
#[derive(Component, Debug, Clone)]
#[cfg_attr(feature = "reflect", derive(Reflect), reflect(Clone, Component, Debug))]
pub struct Cooldown {
pub actuation: f32,
pub time_kind: TimeKind,
timer: Timer,
actuated: bool,
}
impl Cooldown {
#[must_use]
pub fn new(duration: f32) -> Self {
let mut timer = Timer::from_seconds(duration, TimerMode::Once);
timer.tick(Duration::from_secs_f32(duration)); Self {
actuation: DEFAULT_ACTUATION,
time_kind: Default::default(),
timer,
actuated: false,
}
}
#[must_use]
pub fn with_actuation(mut self, actuation: f32) -> Self {
self.actuation = actuation;
self
}
#[must_use]
pub fn with_time_kind(mut self, kind: TimeKind) -> Self {
self.time_kind = kind;
self
}
#[must_use]
pub fn timer(&self) -> &Timer {
&self.timer
}
}
impl InputCondition for Cooldown {
fn evaluate(
&mut self,
_action: &ActionsQuery,
time: &ContextTime,
value: ActionValue,
) -> TriggerState {
let last_actuated = self.actuated;
self.actuated = value.is_actuated(self.actuation);
if !self.actuated {
let finished_before = self.timer.is_finished();
self.timer.tick(time.delta_kind(self.time_kind));
if last_actuated && finished_before {
self.timer.reset();
}
}
if self.actuated && self.timer.is_finished() {
TriggerState::Fired
} else {
TriggerState::None
}
}
fn kind(&self) -> ConditionKind {
ConditionKind::Implicit
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context;
#[test]
fn cooldown() {
let (mut world, mut state) = context::init_world();
let (time, actions) = state.get(&world);
let mut condition = Cooldown::new(1.0);
assert_eq!(
condition.evaluate(&actions, &time, true.into()),
TriggerState::Fired,
"should fire on the first actuation",
);
assert_eq!(
condition.evaluate(&actions, &time, true.into()),
TriggerState::Fired,
"should continue to fire while the input is actuated",
);
assert_eq!(
condition.evaluate(&actions, &time, false.into()),
TriggerState::None,
);
assert_eq!(
condition.evaluate(&actions, &time, true.into()),
TriggerState::None,
"shouldn't fire due to cooldown"
);
world
.resource_mut::<Time<Real>>()
.advance_by(Duration::from_secs(1));
let (time, actions) = state.get(&world);
assert_eq!(
condition.evaluate(&actions, &time, false.into()),
TriggerState::None,
"should fire only when actuated"
);
assert_eq!(
condition.evaluate(&actions, &time, true.into()),
TriggerState::Fired,
);
}
}