1use std::time::{Duration, Instant};
12
13#[derive(Debug)]
27pub struct Time {
28 start: Instant,
29 last_frame: Option<Instant>,
30 delta: f32,
31 raw_delta: f32,
32 elapsed: f64,
33 frame: u64,
34 scale: f32,
35 max_delta: f32,
36 fps: f32,
37 schedule: Vec<Scheduled>,
38 fired: Vec<String>,
39}
40
41#[derive(Debug)]
42struct Scheduled {
43 name: String,
44 at: f64,
45 repeat: Option<f64>,
46}
47
48impl Default for Time {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl Time {
55 pub(crate) fn new() -> Self {
56 Self {
57 start: Instant::now(),
58 last_frame: None,
59 delta: 0.0,
60 raw_delta: 0.0,
61 elapsed: 0.0,
62 frame: 0,
63 scale: 1.0,
64 max_delta: 0.25,
65 fps: 0.0,
66 schedule: Vec::new(),
67 fired: Vec::new(),
68 }
69 }
70
71 pub(crate) fn advance(&mut self, now: Instant) {
73 let raw = match self.last_frame {
74 Some(last) => now.duration_since(last).as_secs_f32(),
75 None => 0.0,
76 };
77 self.last_frame = Some(now);
78 self.raw_delta = raw.min(self.max_delta);
80 self.delta = self.raw_delta * self.scale;
81 self.elapsed += self.delta as f64;
82 self.frame += 1;
83 if raw > 0.0 {
84 let current = 1.0 / raw;
86 self.fps = if self.fps == 0.0 { current } else { self.fps * 0.9 + current * 0.1 };
87 }
88
89 self.fired.clear();
90 let now = self.elapsed;
91 let mut i = 0;
92 while i < self.schedule.len() {
93 if self.schedule[i].at <= now {
94 self.fired.push(self.schedule[i].name.clone());
95 match self.schedule[i].repeat {
96 Some(every) => self.schedule[i].at += every,
97 None => {
98 self.schedule.swap_remove(i);
99 continue;
100 }
101 }
102 }
103 i += 1;
104 }
105 }
106
107 pub fn delta(&self) -> f32 {
112 self.delta
113 }
114
115 pub fn real_delta(&self) -> f32 {
117 self.raw_delta
118 }
119
120 pub fn elapsed(&self) -> f64 {
122 self.elapsed
123 }
124
125 pub fn real_elapsed(&self) -> Duration {
127 self.start.elapsed()
128 }
129
130 pub fn frame(&self) -> u64 {
132 self.frame
133 }
134
135 pub fn fps(&self) -> f32 {
137 self.fps
138 }
139
140 pub fn scale(&self) -> f32 {
142 self.scale
143 }
144
145 pub fn set_scale(&mut self, scale: f32) {
148 self.scale = scale.max(0.0);
149 }
150
151 pub fn pause(&mut self) {
153 self.scale = 0.0;
154 }
155
156 pub fn resume(&mut self) {
158 self.scale = 1.0;
159 }
160
161 pub fn is_paused(&self) -> bool {
163 self.scale == 0.0
164 }
165
166 pub fn after(&mut self, name: impl Into<String>, seconds: f32) {
169 let name = name.into();
170 self.cancel(&name);
171 self.schedule.push(Scheduled { at: self.elapsed + seconds as f64, name, repeat: None });
172 }
173
174 pub fn every(&mut self, name: impl Into<String>, seconds: f32) {
177 let name = name.into();
178 let seconds = (seconds as f64).max(0.001);
179 self.cancel(&name);
180 self.schedule.push(Scheduled { at: self.elapsed + seconds, name, repeat: Some(seconds) });
181 }
182
183 pub fn cancel(&mut self, name: &str) {
185 self.schedule.retain(|s| s.name != name);
186 }
187
188 pub fn is_scheduled(&self, name: &str) -> bool {
190 self.schedule.iter().any(|s| s.name == name)
191 }
192
193 pub fn remaining(&self, name: &str) -> Option<f32> {
195 self.schedule.iter().find(|s| s.name == name).map(|s| (s.at - self.elapsed).max(0.0) as f32)
196 }
197
198 pub fn fired(&self, name: &str) -> bool {
200 self.fired.iter().any(|n| n == name)
201 }
202
203 pub fn fired_events(&self) -> impl Iterator<Item = &str> {
205 self.fired.iter().map(String::as_str)
206 }
207}
208
209#[derive(Debug, Clone)]
220pub struct Timer {
221 started: Instant,
222 offset: Duration,
223 paused_at: Option<Instant>,
224}
225
226impl Default for Timer {
227 fn default() -> Self {
228 Self::new()
229 }
230}
231
232impl Timer {
233 pub fn new() -> Self {
235 Self { started: Instant::now(), offset: Duration::ZERO, paused_at: None }
236 }
237
238 pub fn elapsed(&self) -> Duration {
240 let end = self.paused_at.unwrap_or_else(Instant::now);
241 end.duration_since(self.started) + self.offset
242 }
243
244 pub fn elapsed_secs(&self) -> f32 {
246 self.elapsed().as_secs_f32()
247 }
248
249 pub fn elapsed_ms(&self) -> u64 {
251 self.elapsed().as_millis() as u64
252 }
253
254 pub fn has_elapsed(&self, seconds: f32) -> bool {
256 self.elapsed_secs() >= seconds
257 }
258
259 pub fn restart(&mut self) {
261 *self = Self::new();
262 }
263
264 pub fn set(&mut self, seconds: f32) {
266 let now = Instant::now();
267 self.started = now;
268 self.offset = Duration::from_secs_f32(seconds.max(0.0));
269 if self.paused_at.is_some() {
270 self.paused_at = Some(now);
271 }
272 }
273
274 pub fn pause(&mut self) {
276 if self.paused_at.is_none() {
277 self.paused_at = Some(Instant::now());
278 }
279 }
280
281 pub fn resume(&mut self) {
283 if let Some(paused) = self.paused_at.take() {
284 self.started += Instant::now().duration_since(paused);
285 }
286 }
287
288 pub fn is_paused(&self) -> bool {
290 self.paused_at.is_some()
291 }
292}
293
294#[derive(Debug, Clone)]
304pub struct Cooldown {
305 length: f32,
306 timer: Timer,
307 used: bool,
308}
309
310impl Cooldown {
311 pub fn new(seconds: f32) -> Self {
313 Self { length: seconds, timer: Timer::new(), used: false }
314 }
315
316 pub fn ready(&self) -> bool {
318 !self.used || self.timer.has_elapsed(self.length)
319 }
320
321 pub fn try_use(&mut self) -> bool {
323 if self.ready() {
324 self.trigger();
325 true
326 } else {
327 false
328 }
329 }
330
331 pub fn trigger(&mut self) {
333 self.used = true;
334 self.timer.restart();
335 }
336
337 pub fn reset(&mut self) {
339 self.used = false;
340 }
341
342 pub fn remaining(&self) -> f32 {
344 if self.ready() { 0.0 } else { self.length - self.timer.elapsed_secs() }
345 }
346
347 pub fn set_length(&mut self, seconds: f32) {
349 self.length = seconds;
350 }
351}
352
353#[derive(Debug, Clone)]
364pub struct Interval {
365 every: f32,
366 timer: Timer,
367}
368
369impl Interval {
370 pub fn new(seconds: f32) -> Self {
372 Self { every: seconds.max(0.001), timer: Timer::new() }
373 }
374
375 pub fn tick(&mut self) -> bool {
378 let elapsed = self.timer.elapsed_secs();
379 if elapsed >= self.every {
380 self.timer.set((elapsed - self.every).min(self.every));
382 true
383 } else {
384 false
385 }
386 }
387
388 pub fn reset(&mut self) {
390 self.timer.restart();
391 }
392
393 pub fn set_interval(&mut self, seconds: f32) {
395 self.every = seconds.max(0.001);
396 }
397
398 pub fn interval(&self) -> f32 {
400 self.every
401 }
402}
403
404#[cfg(test)]
405mod tests {
406 use super::*;
407
408 #[test]
409 fn scheduler_fires_once_and_repeats() {
410 let mut t = Time::new();
411 let base = Instant::now();
412 t.advance(base);
413 t.after("once", 0.1);
414 t.every("rep", 0.05);
415 t.advance(base + Duration::from_millis(60));
416 assert!(!t.fired("once"));
417 assert!(t.fired("rep"));
418 t.advance(base + Duration::from_millis(120));
419 assert!(t.fired("once"));
420 assert!(t.fired("rep"));
421 assert!(!t.is_scheduled("once"));
422 assert!(t.is_scheduled("rep"));
423 }
424
425 #[test]
426 fn scale_pauses_game_time() {
427 let mut t = Time::new();
428 let base = Instant::now();
429 t.advance(base);
430 t.pause();
431 t.advance(base + Duration::from_millis(100));
432 assert_eq!(t.delta(), 0.0);
433 assert!(t.real_delta() > 0.0);
434 }
435
436 #[test]
437 fn cooldown_blocks() {
438 let mut c = Cooldown::new(10.0);
439 assert!(c.try_use());
440 assert!(!c.try_use());
441 c.reset();
442 assert!(c.ready());
443 }
444}