1use bevy::prelude::{
8 in_state, App, Commands, Component, Entity, EntityEvent, IntoScheduleConfigs, Plugin, Query, Res, States,
9 Time, Timer, TimerMode, Update,
10};
11
12macro_rules! plugin_systems {
13 ( ) => {
14 (auto_tick)
15 };
16}
17
18#[derive(Default)]
20pub struct AutoTimerPlugin<T>
21where
22 T: States,
23{
24 pub states: Vec<T>,
26}
27
28impl<T> Plugin for AutoTimerPlugin<T>
29where
30 T: States,
31{
32 fn build(&self, app: &mut App) {
33 if self.states.is_empty() {
34 app.add_systems(Update, plugin_systems!());
35 } else {
36 for state in self.states.iter() {
37 app.add_systems(Update, plugin_systems!().run_if(in_state(state.clone())));
38 }
39 }
40 }
41}
42
43impl<T> AutoTimerPlugin<T>
44where
45 T: States,
46{
47 pub fn new(states: Vec<T>) -> Self {
48 Self { states }
49 }
50
51 pub fn any() -> Self {
52 Self::new(Vec::new())
53 }
54}
55
56#[derive(States, Clone, Debug, Hash, Eq, PartialEq)]
59pub enum DummyState {}
60
61#[derive(Default)]
70pub struct AutoTimerPluginAnyState;
71
72impl AutoTimerPluginAnyState {
73 pub fn any() -> AutoTimerPlugin<DummyState> {
74 AutoTimerPlugin::new(Vec::new())
75 }
76}
77
78#[derive(Default)]
79pub enum ActionOnFinish {
80 #[default]
81 Nothing,
83 Despawn,
85 Remove,
87}
88
89#[derive(Component, Default)]
91pub struct AutoTimer {
92 pub timer: Timer,
93 pub action_on_finish: ActionOnFinish,
94}
95
96#[derive(EntityEvent)]
98pub struct AutoTimerFinished {
99 pub entity: Entity,
100}
101
102impl AutoTimer {
103 pub fn from_seconds(duration: f32, mode: TimerMode) -> Self {
104 Self {
105 timer: Timer::from_seconds(duration, mode),
106 ..Self::default()
107 }
108 }
109
110 pub fn progress(&self) -> f32 {
112 self.timer.elapsed().as_secs_f32() / self.timer.duration().as_secs_f32()
113 }
114}
115
116fn auto_tick(mut commands: Commands, time: Res<Time>, mut query: Query<(&mut AutoTimer, Entity)>) {
117 for (mut timer, e) in query.iter_mut() {
118 timer.timer.tick(time.delta());
119 if timer.timer.just_finished() {
120 commands.trigger(AutoTimerFinished { entity: e });
121 match timer.action_on_finish {
122 ActionOnFinish::Nothing => {}
123 ActionOnFinish::Despawn => {
124 commands.entity(e).despawn();
125 }
126 ActionOnFinish::Remove => {
127 commands.entity(e).remove::<AutoTimer>();
128 }
129 }
130 }
131 }
132}