flake/misc/timer.rs
1use std::cell::RefCell;
2use std::pin::Pin;
3use std::rc::Rc;
4use std::task::{Context, Poll};
5use crate::prelude::add_timer;
6
7/// # Timer
8///
9/// A timer that counts down each frame. Can be paused and extended.
10/// Actions can be boundd to the timer that will run on completion.
11///
12/// Don't put the timer in the loop
13///
14/// Timers won't run by default, you must call `add_timer()` and pass in the timer
15/// to add them to the timer list. Or call `spawn_timer(seconds)` and it will run immediately.
16///
17/// ```
18/// let mut my_timer = add_timer(Timer::new(5.0));
19/// // or
20/// let mut my_timer = spawn_timer(5.0);
21/// ```
22///
23/// ```
24/// println!("Time left: {}", my_timer.time_left);
25/// println!("Timer is paused: {}", my_timer.paused);
26/// ```
27///
28/// ```
29/// my_timer.extend(2.0); // extend by 2 seconds
30///
31/// if my_timer.completed() {
32/// // timer is completed
33/// }
34///
35/// if my_timer.ongoing() {
36/// // timer is active
37/// }
38///
39/// // Pause/Unpause a timer
40/// my_timer.pause();
41/// my_timer.unpause();
42/// my_timer.set_paused(true);
43/// my_timer.toggle_paused();
44///
45/// // Action will run if the timer is completed (must be called each frame)
46/// my_timer.on_completion(|| {
47/// println!("Timer was completed!");
48/// })
49///
50/// // Reset the time left to the original (does not include `extend()`)
51/// my_timer.reset();
52///
53/// // Stop the timer (marks as completed())
54/// my_timer.finish();
55/// ```
56///
57/// Once a timer is completed it will be removed from the timer list.
58/// The timer will stop ticking and `on_completion()` will stop firing.
59///
60/// ```
61/// // Re-add a timer to the timer list (REMEMBER TO RESET IT)
62/// my_timer.reset();
63/// add_timer(my_timer);
64/// ```
65#[derive(Default, Clone, PartialEq, PartialOrd)]
66pub struct Timer(pub Rc<RefCell<TimerData>>);
67
68#[derive(Default, Clone, Copy, PartialEq, PartialOrd)]
69pub struct TimerData {
70 original_time: f32,
71 pub time_left: f32,
72
73 pub paused: bool,
74 pub is_active: bool,
75 pub repeating: bool,
76}
77
78impl Future for Timer {
79 type Output = ();
80
81 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
82 if self.completed() {
83 Poll::Ready(())
84 } else {
85 Poll::Pending
86 }
87 }
88}
89
90impl Timer {
91 /// Create a new timer using seconds
92 pub fn new(seconds: f32) -> Self {
93 Self(Rc::new(RefCell::new(TimerData {
94 original_time: seconds,
95 time_left: seconds,
96
97 paused: false,
98 is_active: false,
99 repeating: false,
100 })))
101 }
102
103 /// Create a new repeating timer using seconds. Restarts after completing
104 pub fn new_repeating(seconds: f32) -> Self {
105 Self(Rc::new(RefCell::new(TimerData {
106 time_left: seconds,
107 paused: false,
108 is_active: false,
109 repeating: true,
110 original_time: seconds,
111 })))
112 }
113
114 /// Tick down the time left by `dt` (deltatime) only if it's unpaused
115 pub fn tick(&mut self, dt: f32) {
116 if !self.0.borrow().paused {
117 self.0.borrow_mut().time_left -= dt;
118 }
119 }
120
121 /// Reset the timer to original time (does not include `extend()`)
122 pub fn reset(&self) {
123 let mut t = self.0.borrow_mut();
124 t.time_left = t.original_time;
125 }
126
127 /// Sets time left to zero (completed) which may also remove the timer from the timer list
128 pub fn finish(&self) {
129 self.0.borrow_mut().time_left = 0.0;
130 }
131
132 /// Pause the timer
133 pub fn pause(&self) {
134 self.0.borrow_mut().paused = true;
135 }
136
137 /// Unpause the timer
138 pub fn unpause(&self) {
139 self.0.borrow_mut().paused = false;
140 }
141
142 /// Set whether timer is paused or not
143 pub fn set_paused(&self, paused: bool) {
144 self.0.borrow_mut().paused = paused;
145 }
146
147 /// Toggle the timer paused/unpaused
148 pub fn toggle_paused(&self) {
149 let mut t = self.0.borrow_mut();
150 t.paused = !t.paused;
151 }
152
153 /// Extend (or decrease) the timer's time left
154 pub fn extend(&self, seconds: f32) {
155 self.0.borrow_mut().time_left += seconds;
156 }
157
158 /// Check if timer is ongoing (time left > 0)
159 pub fn ongoing(&self) -> bool {
160 self.0.borrow().time_left > 0.0
161 }
162
163 /// Check if timer is active (in the timer list)
164 pub fn is_active(&self) -> bool {
165 self.0.borrow().is_active
166 }
167
168 /// Runs an action if timer is completed (only runs if timer is in timer list)
169 pub fn on_completion(&self, mut action: impl FnMut()) {
170 if !self.ongoing() && self.0.borrow().is_active {
171 action()
172 }
173 }
174
175 /// Returns `true` if the timer was just completed this frame
176 pub fn completed(&self) -> bool {
177 !self.ongoing() && self.0.borrow().is_active
178 }
179
180 /// Restarts the timer and adds it to the timer list
181 pub fn restart(&self) -> Timer {
182 self.reset();
183 self.start()
184 }
185
186 /// Restarts the timer if it just got completed this frame,
187 /// also returns whether its `completed()` or not
188 pub fn completed_and_restart(&self) -> bool {
189 let completed = self.completed();
190 if completed {
191 self.restart();
192 }
193 completed
194 }
195
196 /// Starts the timer (adds it to the timer list)
197 pub fn start(&self) -> Timer {
198 add_timer(self.clone())
199 }
200
201 pub async fn wait(&self) {
202 self.clone().await
203 }
204}