rustorio_engine/
tick.rs

1use std::fmt::Display;
2
3/// The tick is used to keep track of time in the game.
4/// You can advance the game using the [`advance`](Tick::advance) method or similar.
5/// Many functions and building methods require a [`Tick`] to be passed in, which allows them to update their state.
6/// If a function takes a [`&mut Tick`](Tick), then the function will take time.
7/// If a function merely takes a [`&Tick`](Tick), or no [`Tick`]s at all, it will never advance the game time.
8#[derive(Debug)]
9pub struct Tick {
10    /// The current tick number.
11    tick: u64,
12    log: bool,
13}
14
15impl Tick {
16    pub(crate) const fn start() -> Self {
17        Self {
18            tick: 0,
19            log: false,
20        }
21    }
22
23    /// Sets whether or not to log on tick advancement.
24    pub const fn log(&mut self, log: bool) {
25        self.log = log;
26    }
27
28    /// Advances the game by one tick.
29    ///
30    /// By default prints the current tick number to the console.
31    /// If you want to disable this, use the [`log`](Tick::log) method.
32    pub fn advance(&mut self) {
33        self.advance_by(1);
34    }
35
36    /// Advances the game by the specified number of ticks.
37    ///
38    /// By default prints the current tick number to the console.
39    /// If you want to disable this, use the [`log`](Tick::log) method.
40    pub fn advance_by(&mut self, ticks: u64) {
41        self.tick = self.tick.checked_add(ticks).expect("Tick overflow. Well done you've found an exploit! Or you would have if `https://github.com/albertsgarde/rustorio/issues/3` hadn't beaten you to it!");
42        if self.log {
43            println!("{self}");
44        }
45    }
46
47    /// Advances the game until the specified tick number is reached.
48    /// Does nothing if the target tick is less than or equal to the current tick.
49    ///
50    /// By default prints the current tick number to the console.
51    /// If you want to disable this, use the [`log`](Tick::log) method.
52    pub fn advance_to_tick(&mut self, target_tick: u64) {
53        if target_tick > self.tick {
54            self.advance_by(target_tick - self.tick);
55        }
56    }
57
58    /// Advances the game until the specified condition is met or the maximum number of ticks has passed.
59    /// Returns `true` if the condition was met, or `false` if the maximum number of ticks was reached first.
60    ///
61    /// By default prints the current tick number to the console every tick.
62    /// If you want to disable this, use the [`log`](Tick::log) method.
63    pub fn advance_until<F>(&mut self, mut condition: F, max_ticks: u64) -> bool
64    where
65        F: FnMut(&Tick) -> bool,
66    {
67        let start_tick = self.tick;
68        while !condition(self) && self.tick - start_tick < max_ticks {
69            self.advance();
70        }
71        self.tick - start_tick < max_ticks
72    }
73
74    /// Returns the current tick number.
75    pub const fn cur(&self) -> u64 {
76        self.tick
77    }
78}
79
80impl From<&Tick> for u64 {
81    fn from(tick: &Tick) -> Self {
82        tick.tick
83    }
84}
85
86impl PartialOrd<u64> for &Tick {
87    fn partial_cmp(&self, other: &u64) -> Option<std::cmp::Ordering> {
88        Some(self.tick.cmp(other))
89    }
90}
91
92impl PartialOrd<&Tick> for u64 {
93    fn partial_cmp(&self, other: &&Tick) -> Option<std::cmp::Ordering> {
94        Some(self.cmp(&other.tick))
95    }
96}
97
98impl PartialEq<u64> for &Tick {
99    fn eq(&self, other: &u64) -> bool {
100        self.tick == *other
101    }
102}
103
104impl PartialEq<&Tick> for u64 {
105    fn eq(&self, other: &&Tick) -> bool {
106        *self == other.tick
107    }
108}
109
110impl Display for Tick {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        write!(f, "Tick {}", self.tick)
113    }
114}