diff --git a/crates/horizon-client/src/biome.rs b/crates/horizon-client/src/biome.rs index 46c1e16ca..afe4672b5 100644 --- a/crates/horizon-client/src/biome.rs +++ b/crates/horizon-client/src/biome.rs @@ -20,12 +20,68 @@ pub struct SpawnEntry { /// A `ConstantInt` count (sampling draws nothing). pub constant: bool, } +/// A `natural_mob_spawns` / `spawn_overrides` entry list. +pub fn parse_entries(list: &Value) -> Vec { + list.as_array() + .into_iter() + .flatten() + .filter_map(|e| { + let count = &e["count"]; + let (min, max, constant) = match count { + Value::Number(n) => { + let n = n.as_u64().unwrap_or(1) as u32; + (n, n, true) + } + c if c["type"].as_str() == Some("minecraft:constant") => { + let n = c["value"].as_u64().unwrap_or(1) as u32; + (n, n, true) + } + c => ( + c["min_inclusive"].as_u64().unwrap_or(1) as u32, + c["max_inclusive"].as_u64().unwrap_or(1) as u32, + false, + ), + }; + Some(SpawnEntry { + entity: e["type"].as_str()?.to_owned(), + weight: e["weight"].as_u64().unwrap_or(1) as u32, + min, + max, + constant, + }) + }) + .collect() +} +/// `MobSpawnSettings.MobSpawnCost`. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct SpawnCost { + pub energy_budget: f64, + pub charge: f64, +} +/// The `spawn_costs` of a `natural_mob_spawns` value. +pub fn parse_costs(costs: &Value) -> HashMap { + costs + .as_object() + .into_iter() + .flatten() + .map(|(k, c)| { + ( + k.clone(), + SpawnCost { + energy_budget: c["energy_budget"].as_f64().unwrap_or(0.0), + charge: c["charge"].as_f64().unwrap_or(0.0), + }, + ) + }) + .collect() +} + #[derive(Clone, Debug)] pub struct BiomeInfo { /// `natural_mob_spawns` by category (creature, monster, ...). pub spawns: HashMap>, /// `natural_mob_spawns` spawn costs by entity type. - pub spawn_costs: HashMap, + pub spawn_costs: HashMap, /// The `gameplay/creature_world_gen_spawn_probability` attribute. pub creature_probability: f32, /// The `gameplay/increased_fire_burnout` attribute (jungles, swamps, peaks, @@ -143,12 +199,12 @@ impl Biomes { }; let categories = &natural["spawns_by_category"]; for (category, entries) in categories.as_object().into_iter().flatten() { - let list = crate::mobs::spawn_selection::parse_entries(entries); + let list = parse_entries(entries); spawns.insert(category.clone(), list); } list.push(BiomeInfo { spawns, - spawn_costs: crate::mobs::spawn_selection::parse_costs(&natural["spawn_costs"]), + spawn_costs: parse_costs(&natural["spawn_costs"]), creature_probability: attributes["minecraft:gameplay/creature_world_gen_spawn_probability"] .as_f64() diff --git a/crates/horizon-client/src/mobs/spawn_selection.rs b/crates/horizon-client/src/mobs/spawn_selection.rs index 4e51722f1..ec257a55e 100644 --- a/crates/horizon-client/src/mobs/spawn_selection.rs +++ b/crates/horizon-client/src/mobs/spawn_selection.rs @@ -23,61 +23,7 @@ use horizon_core::{ use serde_json::Value; use std::{collections::HashMap, fs, path::Path}; -/// A `natural_mob_spawns` / `spawn_overrides` entry list. -pub fn parse_entries(list: &Value) -> Vec { - list.as_array() - .into_iter() - .flatten() - .filter_map(|e| { - let count = &e["count"]; - let (min, max, constant) = match count { - Value::Number(n) => { - let n = n.as_u64().unwrap_or(1) as u32; - (n, n, true) - } - c if c["type"].as_str() == Some("minecraft:constant") => { - let n = c["value"].as_u64().unwrap_or(1) as u32; - (n, n, true) - } - c => ( - c["min_inclusive"].as_u64().unwrap_or(1) as u32, - c["max_inclusive"].as_u64().unwrap_or(1) as u32, - false, - ), - }; - Some(SpawnEntry { - entity: e["type"].as_str()?.to_owned(), - weight: e["weight"].as_u64().unwrap_or(1) as u32, - min, - max, - constant, - }) - }) - .collect() -} -/// `MobSpawnSettings.MobSpawnCost`. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct SpawnCost { - pub energy_budget: f64, - pub charge: f64, -} -/// The `spawn_costs` of a `natural_mob_spawns` value. -pub fn parse_costs(costs: &Value) -> HashMap { - costs - .as_object() - .into_iter() - .flatten() - .map(|(k, c)| { - ( - k.clone(), - SpawnCost { - energy_budget: c["energy_budget"].as_f64().unwrap_or(0.0), - charge: c["charge"].as_f64().unwrap_or(0.0), - }, - ) - }) - .collect() -} +pub use crate::biome::{SpawnCost, parse_costs, parse_entries}; /// `MobCategory`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] diff --git a/tools/test_natural_spawn_parity.py b/tools/test_natural_spawn_parity.py index bd3eb2540..8a17c937b 100644 --- a/tools/test_natural_spawn_parity.py +++ b/tools/test_natural_spawn_parity.py @@ -552,6 +552,7 @@ def main(): oracle = lab.ROOT / 'mods/horizon/src/test/java/dev/horizonlab/parity/VanillaNaturalSpawnOracle.java' sources = [oracle, Path(__file__), lab.ROOT / 'crates/horizon-client/examples/probe/natural_spawn_fixture.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_selection.rs', + lab.ROOT / 'crates/horizon-client/src/biome.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_rules.rs'] tested = base.source_hashes(sources) inputs = build / 'inputs.txt' diff --git a/tools/test_spawner_parity.py b/tools/test_spawner_parity.py index 34f6ec726..fc2ea600b 100644 --- a/tools/test_spawner_parity.py +++ b/tools/test_spawner_parity.py @@ -233,6 +233,7 @@ def main(): sources = [oracle, Path(__file__), lab.ROOT / 'crates/horizon-client/examples/probe/natural_spawn_fixture.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawner.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_selection.rs', + lab.ROOT / 'crates/horizon-client/src/biome.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_rules.rs'] tested = base.source_hashes(sources) inputs = build / 'inputs.txt' diff --git a/tools/test_trial_spawner_parity.py b/tools/test_trial_spawner_parity.py index 43d26338a..9e47063e4 100644 --- a/tools/test_trial_spawner_parity.py +++ b/tools/test_trial_spawner_parity.py @@ -198,6 +198,7 @@ def main(): lab.ROOT / 'crates/horizon-client/src/mobs/trial_spawner.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawner.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_selection.rs', + lab.ROOT / 'crates/horizon-client/src/biome.rs', lab.ROOT / 'crates/horizon-client/src/mobs/spawn_rules.rs'] tested = base.source_hashes(sources) inputs = build / 'inputs.txt'