use crate::ReflectComponent;
use crate::registry::EffectMergeRegistry;
use bevy_app::{App, Plugin, PreUpdate};
use bevy_ecs::component::Mutable;
use bevy_ecs::prelude::{Commands, Component, Entity, EntityRef, Query, Res};
use bevy_ecs::schedule::IntoScheduleConfigs;
use bevy_ecs::world::EntityWorldMut;
use bevy_reflect::Reflect;
use bevy_time::{Time, Timer, TimerMode};
use std::fmt::Debug;
use std::time::Duration;
pub(crate) struct TimerPlugin;
impl Plugin for TimerPlugin {
fn build(&self, app: &mut App) {
app.add_systems(PreUpdate, (despawn_finished_lifetimes, tick_delay).chain());
app.world_mut()
.get_resource_or_init::<EffectMergeRegistry>()
.register::<Lifetime>(merge_effect_timer::<Lifetime>)
.register::<Delay>(merge_effect_timer::<Delay>);
}
}
pub fn merge_effect_timer<T: EffectTimer + Component<Mutability = Mutable>>(
mut existing: EntityWorldMut,
incoming: EntityRef,
) {
let incoming = incoming.get::<T>().unwrap();
existing.get_mut::<T>().unwrap().merge(incoming);
}
pub trait EffectTimer: Clone {
fn new(duration: Duration) -> Self;
fn from_seconds(seconds: f32) -> Self {
Self::new(Duration::from_secs_f32(seconds))
}
fn with_mode(self, mode: TimerMergeMode) -> Self;
fn get_timer(&self) -> &Timer;
fn get_timer_mut(&mut self) -> &mut Timer;
fn get_mode(&self) -> &TimerMergeMode;
fn get_mode_mut(&mut self) -> &mut TimerMergeMode;
fn merge(&mut self, incoming: &Self) {
match self.get_mode() {
TimerMergeMode::Replace => self.clone_from(incoming),
TimerMergeMode::Keep => {}
TimerMergeMode::Fraction => {
let fraction = self.get_timer().fraction();
let duration = incoming.get_timer().duration().as_secs_f32();
self.clone_from(incoming);
self.get_timer_mut()
.set_elapsed(Duration::from_secs_f32(fraction * duration));
}
TimerMergeMode::Max => {
let old = self.get_timer().remaining_secs();
let new = incoming.get_timer().remaining_secs();
if new > old {
self.clone_from(incoming);
}
}
TimerMergeMode::Sum => {
let duration = self.get_timer().duration() + incoming.get_timer().duration();
self.clone_from(incoming);
self.get_timer_mut().set_duration(duration);
}
}
}
}
macro_rules! impl_effect_timer {
($ident:ident, $timer_mode:expr) => {
impl EffectTimer for $ident {
fn new(duration: Duration) -> Self {
Self {
timer: Timer::new(duration, $timer_mode),
..Self::default()
}
}
fn with_mode(mut self, mode: TimerMergeMode) -> Self {
self.mode = mode;
self
}
fn get_timer(&self) -> &Timer {
&self.timer
}
fn get_timer_mut(&mut self) -> &mut Timer {
&mut self.timer
}
fn get_mode(&self) -> &TimerMergeMode {
&self.mode
}
fn get_mode_mut(&mut self) -> &mut TimerMergeMode {
&mut self.mode
}
}
};
}
#[doc(alias = "Duration")]
#[derive(Component, Reflect, Eq, PartialEq, Debug, Clone)]
#[reflect(Component, PartialEq, Debug, Clone)]
pub struct Lifetime {
pub timer: Timer,
pub mode: TimerMergeMode,
}
impl_effect_timer!(Lifetime, TimerMode::Once);
impl Default for Lifetime {
fn default() -> Self {
Self {
timer: Timer::default(),
mode: TimerMergeMode::Max,
}
}
}
#[derive(Component, Reflect, Eq, PartialEq, Debug, Clone)]
#[reflect(Component, PartialEq, Debug, Clone)]
pub struct Delay {
pub timer: Timer,
pub mode: TimerMergeMode,
}
impl_effect_timer!(Delay, TimerMode::Repeating);
impl Delay {
#[doc(alias = "trigger_on_start", alias = "almost_finish")]
pub fn trigger_immediately(mut self) -> Self {
self.timer.almost_finish();
self
}
}
impl Default for Delay {
fn default() -> Self {
Self {
timer: Timer::default(),
mode: TimerMergeMode::Fraction,
}
}
}
#[derive(Reflect, Eq, PartialEq, Debug, Copy, Clone)]
#[reflect(PartialEq, Debug, Clone)]
pub enum TimerMergeMode {
Replace,
Keep,
Fraction,
Max,
Sum,
}
pub(super) fn despawn_finished_lifetimes(
mut commands: Commands,
time: Res<Time>,
mut query: Query<(Entity, &mut Lifetime)>,
) {
for (entity, mut lifetime) in &mut query {
lifetime.timer.tick(time.delta());
if lifetime.timer.is_finished() {
commands.entity(entity).despawn();
}
}
}
pub(super) fn tick_delay(time: Res<Time>, mut query: Query<&mut Delay>) {
for mut delay in &mut query {
delay.timer.tick(time.delta());
}
}