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
//! 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;
}

/// 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 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 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);
        }
    }
}