screeps/constants/
seasonal.rs

1//! Constants that are unique to object types or mechanics found in Screeps
2//! seasonal.
3//!
4//! [Screeps seasonal documentation](https://docs-season.screeps.com/api/#Season-constants)
5
6/// Constants for Screeps seasonal, season 1
7///
8/// These constants are relevant to the mechanics in seasonal, season 1.
9/// [This mod](https://github.com/screeps/mod-season1) can be used to enable these mechanics
10/// on a private server.
11#[cfg(feature = "seasonal-season-1")]
12pub mod season_1 {
13    // RESOURCE_SCORE defined in `types.rs`
14    // FIND_SCORE_CONTAINERS / FIND_SCORE_COLLECTORS defined in `find.rs`
15    // LOOK_SCORE_CONTAINERS / LOOK_SCORE_COLLECTORS defined in `look.rs`
16    /// Radius around each [`ScoreCollector`] that starts surrounded with
17    /// [`StructureWall`]s with random hit points.
18    ///
19    /// [`ScoreCollector`]: crate::objects::ScoreCollector
20    /// [`StructureWall`]: crate::objects::StructureWall
21    pub const WALLS_RADIUS: u8 = 5;
22    /// The percentage chance that a given room will have a [`ScoreContainer`]
23    /// spawned in it every [`SCORE_CONTAINER_SPAWN_INTERVAL_TICKS`] ticks.
24    ///
25    /// [`ScoreContainer`]: crate::objects::ScoreContainer
26    pub const SCORE_CONTAINER_SPAWN_CHANCE: f32 = 0.01;
27    // SCORE_CONTAINER_SPAWN_INTERVAL not implemented due to being no longer
28    // used as of https://github.com/screeps/mod-season1/commit/3b4d0aaabfb4bffab80845ac4ea9611f83935e1c
29    /// The number of ticks between chances to spawn a [`ScoreContainer`] in
30    /// rooms at random.
31    ///
32    /// [`ScoreContainer`]: crate::objects::ScoreContainer
33    pub const SCORE_CONTAINER_SPAWN_INTERVAL_TICKS: u32 = 250;
34    /// Amount of capacity in a [`ScoreCollector`] that regenerates each tick.
35    ///
36    /// [`ScoreCollector`]: crate::objects::ScoreCollector
37    pub const SCORE_COLLECTOR_SINK: u32 = 20;
38    /// Maximum capacity bucket for a [`ScoreCollector`].
39    ///
40    /// [`ScoreCollector`]: crate::objects::ScoreCollector
41    pub const SCORE_COLLECTOR_MAX_CAPACITY: u32 = 20_000;
42
43    // extra constants for s1 related to the score spawn bonus/crisis cycle
44    // https://github.com/screeps/mod-season1/blob/7ca3c7ddb47bf9dfbdfb4e72b666a3159fde8780/src/scoreContainer.roomObject.js
45    /// The duration of a full score cycle.
46    pub const SCORE_CYCLE_DURATION: u32 = 50_000;
47
48    /// The point of the score cycle where bonus time begins, multiplying
49    /// spawned score by [`SCORE_CYCLE_BONUS_MULTIPLIER`].
50    pub const SCORE_CYCLE_BONUS_START: u32 = 45_000;
51
52    /// The point of the score cycle where bonus time ends.
53    pub const SCORE_CYCLE_BONUS_END: u32 = 50_000;
54
55    /// The multiplier for score spawned during bonus time.
56    pub const SCORE_CYCLE_BONUS_MULTIPLIER: u8 = 2;
57
58    /// The point of the score cycle where crisis time begins, during which no
59    /// new [`ScoreContainer`]s will be spawned.
60    ///
61    /// [`ScoreContainer`]: crate::objects::ScoreContainer
62    pub const SCORE_CYCLE_CRISIS_START: u32 = 10_000;
63
64    /// The point of the score cycle where crisis time ends, allowing
65    /// [`ScoreContainer`]s to be spawned once again.
66    ///
67    /// [`ScoreContainer`]: crate::objects::ScoreContainer
68    pub const SCORE_CYCLE_CRISIS_END: u32 = 15_000;
69
70    /// The multiplier for score spawned during crisis time.
71    pub const SCORE_CYCLE_CRISIS_MULTIPLIER: u8 = 0;
72
73    /// The minimum amount of ticks a [`ScoreContainer`] can exist before
74    /// despawning.
75    ///
76    /// [`ScoreContainer`]: crate::objects::ScoreContainer
77    // https://github.com/screeps/mod-season1/blob/7ca3c7ddb47bf9dfbdfb4e72b666a3159fde8780/src/scoreContainer.roomObject.js#L93
78    pub const SCORE_MIN_DECAY: u16 = 500;
79
80    /// The maximum amount of ticks a [`ScoreContainer`] can exist before
81    /// despawning.
82    ///
83    /// [`ScoreContainer`]: crate::objects::ScoreContainer
84    pub const SCORE_MAX_DECAY: u16 = 5_000;
85
86    /// The different periods in the score cycle.
87    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
88    pub enum ScoreCycleState {
89        /// Normal score spawns during the normal parts of the cycle
90        Normal,
91        /// No score spawns during the crisis period
92        Crisis,
93        /// Double score in each container during the bonus period
94        Bonus,
95    }
96
97    impl ScoreCycleState {
98        /// Gets the multiplier of score associated with the represented part of
99        /// the score cycle.
100        pub fn multiplier(&self) -> u8 {
101            match self {
102                ScoreCycleState::Normal => 1,
103                ScoreCycleState::Crisis => SCORE_CYCLE_CRISIS_MULTIPLIER,
104                ScoreCycleState::Bonus => SCORE_CYCLE_BONUS_MULTIPLIER,
105            }
106        }
107    }
108
109    /// Calculates the state of the score cycle for season 1
110    pub const fn score_cycle_at_tick(tick: u32) -> ScoreCycleState {
111        #[allow(clippy::match_overlapping_arm)]
112        match tick % SCORE_CYCLE_DURATION {
113            // the bonus/crisis periods are exclusive of their boundaries
114            // https://github.com/screeps/mod-season1/blob/7ca3c7ddb47bf9dfbdfb4e72b666a3159fde8780/src/scoreContainer.roomObject.js#L77-L81
115            // match on those exact values first
116            SCORE_CYCLE_CRISIS_START => ScoreCycleState::Normal,
117            SCORE_CYCLE_BONUS_START => ScoreCycleState::Normal,
118            // then on the remaining ranges
119            SCORE_CYCLE_CRISIS_START..SCORE_CYCLE_CRISIS_END => ScoreCycleState::Crisis,
120            SCORE_CYCLE_BONUS_START..SCORE_CYCLE_BONUS_END => ScoreCycleState::Bonus,
121            _ => ScoreCycleState::Normal,
122        }
123    }
124
125    /// Calculates the state of the score cycle for season 7, which reverses the
126    /// crisis/bonus order
127    pub const fn s7_score_cycle_at_tick(tick: u32) -> ScoreCycleState {
128        #[allow(clippy::match_overlapping_arm)]
129        match tick % SCORE_CYCLE_DURATION {
130            // the bonus/crisis periods are exclusive of their boundaries
131            // https://github.com/screeps/mod-season1/blob/7ca3c7ddb47bf9dfbdfb4e72b666a3159fde8780/src/scoreContainer.roomObject.js#L77-L81
132            // match on those exact values first
133            SCORE_CYCLE_CRISIS_START => ScoreCycleState::Normal,
134            SCORE_CYCLE_BONUS_START => ScoreCycleState::Normal,
135            // then on the remaining ranges - these are flipped from normal in s7, which is
136            // not currently open sourced
137            SCORE_CYCLE_CRISIS_START..SCORE_CYCLE_CRISIS_END => ScoreCycleState::Bonus,
138            SCORE_CYCLE_BONUS_START..SCORE_CYCLE_BONUS_END => ScoreCycleState::Crisis,
139            _ => ScoreCycleState::Normal,
140        }
141    }
142
143    #[cfg(test)]
144    mod test {
145        use super::{s7_score_cycle_at_tick, score_cycle_at_tick, ScoreCycleState};
146
147        #[test]
148        fn s1_score_cycle() {
149            assert_eq!(score_cycle_at_tick(0), ScoreCycleState::Normal);
150            assert_eq!(score_cycle_at_tick(10_000), ScoreCycleState::Normal);
151            assert_eq!(score_cycle_at_tick(10_001), ScoreCycleState::Crisis);
152            assert_eq!(score_cycle_at_tick(14_999), ScoreCycleState::Crisis);
153            assert_eq!(score_cycle_at_tick(15_000), ScoreCycleState::Normal);
154            assert_eq!(score_cycle_at_tick(45_000), ScoreCycleState::Normal);
155            assert_eq!(score_cycle_at_tick(45_001), ScoreCycleState::Bonus);
156            assert_eq!(score_cycle_at_tick(49_999), ScoreCycleState::Bonus);
157            assert_eq!(score_cycle_at_tick(50_000), ScoreCycleState::Normal);
158
159            assert_eq!(score_cycle_at_tick(200_000), ScoreCycleState::Normal);
160            assert_eq!(score_cycle_at_tick(210_000), ScoreCycleState::Normal);
161            assert_eq!(score_cycle_at_tick(210_001), ScoreCycleState::Crisis);
162            assert_eq!(score_cycle_at_tick(214_999), ScoreCycleState::Crisis);
163            assert_eq!(score_cycle_at_tick(215_000), ScoreCycleState::Normal);
164            assert_eq!(score_cycle_at_tick(245_000), ScoreCycleState::Normal);
165            assert_eq!(score_cycle_at_tick(245_001), ScoreCycleState::Bonus);
166            assert_eq!(score_cycle_at_tick(249_999), ScoreCycleState::Bonus);
167            assert_eq!(score_cycle_at_tick(250_000), ScoreCycleState::Normal);
168        }
169
170        #[test]
171        fn s7_score_cycle() {
172            assert_eq!(s7_score_cycle_at_tick(0), ScoreCycleState::Normal);
173            assert_eq!(s7_score_cycle_at_tick(10_000), ScoreCycleState::Normal);
174            assert_eq!(s7_score_cycle_at_tick(10_001), ScoreCycleState::Bonus);
175            assert_eq!(s7_score_cycle_at_tick(14_999), ScoreCycleState::Bonus);
176            assert_eq!(s7_score_cycle_at_tick(15_000), ScoreCycleState::Normal);
177            assert_eq!(s7_score_cycle_at_tick(45_000), ScoreCycleState::Normal);
178            assert_eq!(s7_score_cycle_at_tick(45_001), ScoreCycleState::Crisis);
179            assert_eq!(s7_score_cycle_at_tick(49_999), ScoreCycleState::Crisis);
180            assert_eq!(s7_score_cycle_at_tick(50_000), ScoreCycleState::Normal);
181
182            assert_eq!(s7_score_cycle_at_tick(200_000), ScoreCycleState::Normal);
183            assert_eq!(s7_score_cycle_at_tick(210_000), ScoreCycleState::Normal);
184            assert_eq!(s7_score_cycle_at_tick(210_001), ScoreCycleState::Bonus);
185            assert_eq!(s7_score_cycle_at_tick(214_999), ScoreCycleState::Bonus);
186            assert_eq!(s7_score_cycle_at_tick(215_000), ScoreCycleState::Normal);
187            assert_eq!(s7_score_cycle_at_tick(245_000), ScoreCycleState::Normal);
188            assert_eq!(s7_score_cycle_at_tick(245_001), ScoreCycleState::Crisis);
189            assert_eq!(s7_score_cycle_at_tick(249_999), ScoreCycleState::Crisis);
190            assert_eq!(s7_score_cycle_at_tick(250_000), ScoreCycleState::Normal);
191        }
192    }
193}
194
195/// Constants for Screeps seasonal, season 2
196///
197/// These constants are relevant to the mechanics in seasonal, season 2.
198/// [This mod](https://github.com/screeps/mod-season2) can be used to enable these mechanics
199/// on a private server.
200#[cfg(feature = "seasonal-season-2")]
201pub mod season_2 {
202    // RESOURCE_SYMBOL_* defined in `types.rs`
203    use crate::constants::ResourceType;
204    /// All of the resources which are 'symbols' that can be taken
205    /// to the correct type of [`SymbolDecoder`] to score points.
206    ///
207    /// [`SymbolDecoder`]: crate::objects::SymbolDecoder
208    pub const SYMBOLS: [ResourceType; 22] = [
209        ResourceType::SymbolAleph,
210        ResourceType::SymbolBeth,
211        ResourceType::SymbolGimmel,
212        ResourceType::SymbolDaleth,
213        ResourceType::SymbolHe,
214        ResourceType::SymbolWaw,
215        ResourceType::SymbolZayin,
216        ResourceType::SymbolHeth,
217        ResourceType::SymbolTeth,
218        ResourceType::SymbolYodh,
219        ResourceType::SymbolKaph,
220        ResourceType::SymbolLamedh,
221        ResourceType::SymbolMem,
222        ResourceType::SymbolNun,
223        ResourceType::SymbolSamekh,
224        ResourceType::SymbolAyin,
225        ResourceType::SymbolPe,
226        ResourceType::SymbolTsade,
227        ResourceType::SymbolQoph,
228        ResourceType::SymbolRes,
229        ResourceType::SymbolSin,
230        ResourceType::SymbolTaw,
231    ];
232    // FIND_SYMBOL_CONTAINERS / FIND_SYMBOL_DECODERS defined in `find.rs`
233    // LOOK_SYMBOL_CONTAINERS / LOOK_SYMBOL_DECODERS defined in `look.rs`
234    /// The percentage chance that a given room will have a [`SymbolContainer`]
235    /// spawned in it every [`SYMBOL_CONTAINER_SPAWN_INTERVAL_TICKS`] ticks.
236    ///
237    /// [`SymbolContainer`]: crate::objects::SymbolContainer
238    pub const SYMBOL_CONTAINER_SPAWN_CHANCE: f32 = 0.01;
239    /// The number of ticks between chances to spawn a [`SymbolContainer`] in
240    /// rooms at random.
241    ///
242    /// [`SymbolContainer`]: crate::objects::SymbolContainer
243    pub const SYMBOL_CONTAINER_SPAWN_INTERVAL_TICKS: u32 = 250;
244    /// Translates the `CONTROLLER_LEVEL_SCORE_MULTIPLIERS` constant, the score
245    /// multipler for the room's [`SymbolDecoder`] depending on the RCL of the
246    /// room.
247    ///
248    /// [`SymbolDecoder`]: crate::objects::SymbolDecoder
249    #[inline]
250    pub const fn controller_level_score_multiplers(rcl: u32) -> u32 {
251        match rcl {
252            4 => 3,
253            5 => 9,
254            6 => 27,
255            7 => 81,
256            8 => 243,
257            _ => 1,
258        }
259    }
260}
261
262// no unique mechanics or constants for seasons 3 (power) or 4 (commodities)
263
264/// Constants for Screeps seasonal, season 5
265///
266/// These constants are relevant to the mechanics in seasonal, season 5.
267/// [This mod](https://github.com/screeps/mod-season5) can be used to enable these mechanics
268/// on a private server.
269#[cfg(feature = "seasonal-season-5")]
270pub mod season_5 {
271    use crate::constants::Density;
272    // RESOURCE_THORIUM defined in `types.rs`
273    // FIND_REACTORS defined in `find.rs`
274    // LOOK_REACTORS defined in `look.rs`
275    /// Capacity of the [`Thorium`] storage of a [`Reactor`].
276    ///
277    /// [`Thorium`]: crate::constants::ResourceType::Thorium
278    /// [`Reactor`]: crate::objects::Reactor
279    // no official constant for this currently, but providing as 'extra' constant
280    // for consistency with prior seasons
281    pub const REACTOR_THORIUM_CAPACITY: u32 = 1_000;
282
283    impl Density {
284        /// Amount of [`Thorium`] generated for each density
285        /// level, replacing the amounts from [`Density::amount`].
286        ///
287        /// [`Thorium`]: crate::constants::ResourceType::Thorium
288        #[inline]
289        pub const fn thorium_amount(self) -> u32 {
290            match self {
291                Density::Low => 10_000,
292                Density::Moderate => 22_000,
293                Density::High => 45_000,
294                Density::Ultra => 67_000,
295            }
296        }
297    }
298
299    /// The added decay each tick for all creeps and decaying structures when
300    /// [`Thorium`] is present on the same tile.
301    ///
302    /// [`Thorium`]: crate::constants::ResourceType::Thorium
303    pub fn thorium_decay(thorium_amount: u32) -> u32 {
304        // Math.floor(Math.log10([total Thorium on the tile]))
305        (thorium_amount as f64).log10().floor() as u32
306    }
307
308    /// The points generated by a [`Reactor`] processing [`Thorium`] each tick,
309    /// which increases based on the number of ticks of continuous operation.
310    ///
311    /// [`Thorium`]: crate::constants::ResourceType::Thorium
312    /// [`Reactor`]: crate::objects::Reactor
313    pub fn reactor_points_per_tick(continuous_work_ticks: u32) -> u32 {
314        // 1 + Math.floor(Math.log10([ticks of continuous operating]))
315        1 + (continuous_work_ticks as f64).log10().floor() as u32
316    }
317
318    #[cfg(test)]
319    mod test {
320        use super::{reactor_points_per_tick, thorium_decay};
321
322        #[test]
323        fn decay_formula() {
324            assert_eq!(thorium_decay(0), 0);
325            // quantities of thorium below 10 do not cause any additional decay
326            assert_eq!(thorium_decay(1), 0);
327            assert_eq!(thorium_decay(9), 0);
328            assert_eq!(thorium_decay(10), 1);
329            assert_eq!(thorium_decay(99), 1);
330            assert_eq!(thorium_decay(100), 2);
331            assert_eq!(thorium_decay(999), 2);
332            assert_eq!(thorium_decay(1000), 3);
333            assert_eq!(thorium_decay(10_000), 4);
334        }
335
336        #[test]
337        fn score_formula() {
338            assert_eq!(reactor_points_per_tick(0), 1);
339            assert_eq!(reactor_points_per_tick(1), 1);
340            assert_eq!(reactor_points_per_tick(9), 1);
341            assert_eq!(reactor_points_per_tick(10), 2);
342            assert_eq!(reactor_points_per_tick(99), 2);
343            assert_eq!(reactor_points_per_tick(100), 3);
344            assert_eq!(reactor_points_per_tick(999), 3);
345            assert_eq!(reactor_points_per_tick(1000), 4);
346            assert_eq!(reactor_points_per_tick(10_000), 5);
347            assert_eq!(reactor_points_per_tick(100_000), 6);
348        }
349    }
350}