Skip to main content
Verticopolis engineGitHub

verticopolis_engine/load/
founder.rs

1//! The founder status test.
2
3use super::*;
4
5/// JS `Number.parseInt(s, 10)` as an option.
6pub(crate) fn parse_int(s: &str) -> Option<f64> {
7    // `parseInt` skips the JavaScript whitespace set, the byte order mark included.
8    let t = js_trim(s);
9    let (neg, rest) = match t.strip_prefix('-') {
10        Some(r) => (true, r),
11        None => (false, t.strip_prefix('+').unwrap_or(t)),
12    };
13    let digits: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
14    if digits.is_empty() {
15        return None;
16    }
17    let v: f64 = digits.parse().ok()?;
18    Some(if neg { -v } else { v })
19}
20
21#[doc(hidden)]
22/// `detectFounder(raw)`.
23pub fn detect_founder(raw: &Value) -> bool {
24    if raw.get("founder") == Some(&Value::Bool(true)) {
25        return true;
26    }
27    raw.get("appVersion")
28        .and_then(Value::as_str)
29        .and_then(parse_int)
30        .is_some_and(|v| v < 2.0)
31}
32
33/// Marks a freshly loaded game as a founder's tower when its file carries no
34/// build stamp, as the web game does. Call it after [`deserialize`] on a file
35/// the player opened; skip it and the next save drops the status.
36pub fn mark_founder_from_loaded_file(sim: &mut Simulation, raw: &Value) {
37    if !sim.founder && raw.get("appVersion").is_none() {
38        sim.founder = true;
39    }
40}