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