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}