diff --git a/decisions/architecture.md b/decisions/architecture.md index 102e7bd39..40900c1d0 100644 --- a/decisions/architecture.md +++ b/decisions/architecture.md @@ -785,8 +785,9 @@ Technical foundation decisions that constrain implementation: engine, client-ser ### D-195: Attractor-Matching Compatibility Matrix for Generative City Placement - **Date:** 2026-05-01 - **Decision:** City placement on a planetary surface uses an attractor-matching model. A `GeographicAttractor` is a terrain feature that increases city placement score at nearby positions. Seven `AttractorType` variants: `RiverMouth`, `CoastalAccess`, `RiverCrossing`, `ValleyFloor`, `PassEntrance`, `LakeShore`, `PlainCenter`. A `CompatibilityMatrix` is a 10×7 scoring table (10 `economic_role` values × 7 attractor types) whose cells are float weights (0.0–3.0) representing how strongly that economic role favors that terrain feature. Examples: manufacturing → RiverMouth 2.8, ValleyFloor 2.1; financial → CoastalAccess 2.5, PlainCenter 1.8; agricultural → ValleyFloor 3.0, PlainCenter 2.5. The matrix is authored data (not computed at runtime). Attractor extraction from heightmaps is defined in D-209. Matching algorithm is defined in D-211. -- **Rationale:** Terrain-naive city placement produces spatially incoherent worlds. The compatibility matrix gives different city types different terrain affinities, so financial hubs appear on coasts and agricultural cities appear in fertile valleys — without hard-coding placement rules per city type. The float weight matrix gives graduated preference, not binary requirement. -- **Ticket:** #919, #925 +- **Rationale:** Terrain-naive city placement produces spatially incoherent worlds. The compatibility matrix gives different city types different terrain affinities, so financial hubs appear on coasts and agricultural cities appear in fertile valleys — without hard-coding placement rules per city type. The weight matrix gives graduated preference, not binary requirement. +- **Amended 2026-06-03 (#955):** the matrix weights and the whole placement-scoring path are **integer basis-points, not f32** (D-010 determinism / D-227 save-critical). When #955 wired `match_cities` into the deterministic generation cascade, the original f32 scoring became a live cross-platform divergence risk (a near-tie score comparison or the Hungarian's f32 reductions can round differently per platform → a different world from the same seed). Now: `CompatibilityMatrix.weights` are `i32` bps (10000 = 1.0×; the examples above are 28000 / 30000 / 25000 …), `GeographicAttractor.strength` and `terrain_modification_cost` are bps, and `cell_score` / the Hungarian / `CityPlacement.score` use integer arithmetic. The 0.0–3.0 affinity semantics are unchanged; only the representation is now integer. +- **Ticket:** #919, #925, #955 - **Raised by:** Generation cascade workshop (#897) - **Cross-reference:** D-191 (atlas pipeline), D-208 (D8 drainage — attractor extraction), D-209 (feature tag extraction), D-211 (attractor-matching pipeline) diff --git a/server/src/atlas/attractor_matching.rs b/server/src/atlas/attractor_matching.rs index dcac69872..c283ecdc8 100644 --- a/server/src/atlas/attractor_matching.rs +++ b/server/src/atlas/attractor_matching.rs @@ -12,8 +12,8 @@ //! 5. Name fulfillment check: warn if any atlas city was not placed. //! //! **Mismatch flagging (D-211):** -//! - score < 0.35 → WARNING -//! - score < 0.15 → ERROR (flagged for manual review; generation continues) +//! - score < 1500 → WARNING +//! - score < 500 → ERROR (flagged for manual review; generation continues) use tracing::{error, warn}; @@ -47,7 +47,8 @@ pub struct CityPlacement { pub city_id: u64, pub position: (u16, u16), pub attractor_type: AttractorType, - pub score: f32, + /// Integer match score (D-010). See [`cell_score`]. + pub score: i64, pub synthetic: bool, } @@ -88,23 +89,27 @@ fn attractor_col(at: &AttractorType) -> usize { } } -/// Compute the raw match score between a city and an attractor. -/// Score = matrix_weight × attractor.strength × (1.0 / terrain_modification_cost). +/// Compute the raw match score between a city and an attractor (integer, D-010). +/// +/// `score = weight(0–100) × strength(0–100) × 100 / cost_pct`, where `cost_pct` +/// is the terrain build cost as a percent of baseline (100 = 1.0×). Higher cost +/// → lower score. All integer — no f32 in any placement decision (#955). fn cell_score( city: &CityRecord, attractor: &GeographicAttractor, matrix: &CompatibilityMatrix, - terrain_cost: f32, -) -> f32 { + terrain_cost_pct: i32, +) -> i64 { let row = role_row(&city.economic_role); let col = attractor_col(&attractor.attractor_type); - let weight = matrix.weights[row][col]; - let cost_factor = if terrain_cost > 0.0 { - 1.0 / terrain_cost + let weight = matrix.weights[row][col] as i64; + let strength = attractor.strength as i64; + let cost = if terrain_cost_pct > 0 { + terrain_cost_pct as i64 } else { - 1.0 + 100 }; - weight * attractor.strength * cost_factor + weight * strength * 100 / cost } // --------------------------------------------------------------------------- @@ -118,7 +123,7 @@ fn cell_score( /// by using `max_score - score` as cost. /// /// Returns `assignment[i] = j` for each row i. -fn hungarian(cost: &[Vec]) -> Vec { +fn hungarian(cost: &[Vec]) -> Vec { let n = cost.len(); if n == 0 { return Vec::new(); @@ -128,25 +133,29 @@ fn hungarian(cost: &[Vec]) -> Vec { return vec![usize::MAX; n]; } + // Sentinel "infinity" — far above any real cost (scores ≤ ~10^4, padded + // costs accumulate well under this) yet far below i64::MAX so the potential + // updates can't overflow. + let inf: i64 = 1 << 60; + // Pad to square n×n if m < n (more cities than attractors handled by overflow). let sz = n.max(m); - let mut c: Vec> = vec![vec![0.0; sz]; sz]; + let mut c: Vec> = vec![vec![0i64; sz]; sz]; for i in 0..n { for j in 0..m { c[i][j] = cost[i][j]; } // Pad extra columns with high cost so overflow cities pick them last. for item in c[i].iter_mut().take(sz).skip(m) { - *item = f32::MAX / 2.0; + *item = inf; } } // Pad extra rows with 0 cost (dummy workers). // Already initialized to 0. // Standard O(n³) Hungarian. - let inf = f32::MAX / 2.0; - let mut u = vec![0.0f32; sz + 1]; - let mut v = vec![0.0f32; sz + 1]; + let mut u = vec![0i64; sz + 1]; + let mut v = vec![0i64; sz + 1]; let mut p = vec![0usize; sz + 1]; // p[j] = row assigned to column j (1-indexed) let mut way = vec![0usize; sz + 1]; @@ -243,9 +252,9 @@ fn synthetic_attractor(placed: &[CityPlacement], grid_w: u32, grid_h: u32) -> Ge GeographicAttractor { position: (row, col), attractor_type: AttractorType::PlainCenter, - strength: 0.5, + strength: 50, sub_biome: SubBiomeVariant::TemperateGrassland, - terrain_modification_cost: 1.0, + terrain_modification_cost: 100, } } @@ -256,16 +265,16 @@ fn synthetic_attractor(placed: &[CityPlacement], grid_w: u32, grid_h: u32) -> Ge /// Run the five-phase attractor-matching pipeline (D-211). /// /// `terrain_costs` maps attractor index → terrain_modification_cost (1.0 = baseline). -/// If `None`, all costs default to 1.0. +/// If `None`, all costs default to 100 (1.0× baseline). pub fn match_cities( cities: &[CityRecord], attractors: &[GeographicAttractor], matrix: &CompatibilityMatrix, - terrain_costs: Option<&[f32]>, + terrain_costs: Option<&[i32]>, grid_w: u32, grid_h: u32, ) -> Vec { - let default_cost = vec![1.0f32; attractors.len()]; + let default_cost = vec![100i32; attractors.len()]; let costs = terrain_costs.unwrap_or(&default_cost); let mut placements: Vec = Vec::with_capacity(cities.len()); @@ -274,7 +283,7 @@ pub fn match_cities( // ------------------------------------------------------------------------- // Phase 1: Score matrix // ------------------------------------------------------------------------- - let scores: Vec> = cities + let scores: Vec> = cities .iter() .map(|city| { attractors @@ -306,7 +315,7 @@ pub fn match_cities( .iter() .enumerate() .filter(|(ai, _)| !used_attractors[*ai]) - .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); + .max_by(|(_, a), (_, b)| a.cmp(b)); if let Some((ai, &score)) = best { used_attractors[ai] = true; @@ -340,12 +349,13 @@ pub fn match_cities( if !tier_bc_indices.is_empty() && !free_attractors.is_empty() { // Build cost sub-matrix (maximization → minimization via complement). let scores_ref = &scores; - let max_score: f32 = tier_bc_indices + let max_score: i64 = tier_bc_indices .iter() .flat_map(|&ci| free_attractors.iter().map(move |&ai| scores_ref[ci][ai])) - .fold(0.0f32, f32::max); + .max() + .unwrap_or(0); - let cost: Vec> = tier_bc_indices + let cost: Vec> = tier_bc_indices .iter() .map(|&ci| { free_attractors @@ -387,7 +397,7 @@ pub fn match_cities( continue; } let synthetic = synthetic_attractor(&placements, grid_w, grid_h); - let score = cell_score(city, &synthetic, matrix, 1.0); + let score = cell_score(city, &synthetic, matrix, 100); flag_mismatch(&city.name, score); placements.push(CityPlacement { city_id: city.city_id, @@ -416,18 +426,20 @@ pub fn match_cities( placements } -fn flag_mismatch(city_name: &str, score: f32) { - if score < 0.15 { +fn flag_mismatch(city_name: &str, score: i64) { + // Integer score space (#955): a baseline-cost perfect match peaks at ~10000, + // neutral ~2500. < 500 is a severe mismatch; < 1500 below expected quality. + if score < 500 { error!( city = %city_name, score, - "attractor mismatch score < 0.15 — flagged for manual review" + "attractor mismatch score < 500 — flagged for manual review" ); - } else if score < 0.35 { + } else if score < 1500 { warn!( city = %city_name, score, - "attractor mismatch score < 0.35 — below expected quality" + "attractor mismatch score < 1500 — below expected quality" ); } } @@ -481,17 +493,17 @@ mod tests { fn uniform_matrix() -> CompatibilityMatrix { CompatibilityMatrix { - weights: [[1.0; 7]; 10], + weights: [[50; 7]; 10], } } - fn make_attractor(row: u16, col: u16, at: AttractorType, strength: f32) -> GeographicAttractor { + fn make_attractor(row: u16, col: u16, at: AttractorType, strength: i32) -> GeographicAttractor { GeographicAttractor { position: (row, col), attractor_type: at, strength, sub_biome: SubBiomeVariant::TemperateGrassland, - terrain_modification_cost: 1.0, + terrain_modification_cost: 100, } } @@ -508,7 +520,7 @@ mod tests { #[test] fn single_city_single_attractor() { let cities = vec![make_city(1, SettlementClass::NameLocked, 500_000)]; - let attractors = vec![make_attractor(10, 20, AttractorType::RiverMouth, 0.8)]; + let attractors = vec![make_attractor(10, 20, AttractorType::RiverMouth, 80)]; let matrix = uniform_matrix(); let placements = match_cities(&cities, &attractors, &matrix, None, 512, 256); assert_eq!(placements.len(), 1); @@ -525,8 +537,8 @@ mod tests { make_city(2, SettlementClass::PopulationBudget, 200_000), ]; let attractors = vec![ - make_attractor(5, 5, AttractorType::RiverMouth, 0.9), // best - make_attractor(10, 10, AttractorType::ValleyFloor, 0.4), // second + make_attractor(5, 5, AttractorType::RiverMouth, 90), // best + make_attractor(10, 10, AttractorType::ValleyFloor, 40), // second ]; let matrix = uniform_matrix(); let placements = match_cities(&cities, &attractors, &matrix, None, 512, 256); @@ -541,7 +553,7 @@ mod tests { make_city(1, SettlementClass::NameLocked, 2_000_000), make_city(2, SettlementClass::PopulationBudget, 60_000), ]; - let attractors = vec![make_attractor(0, 0, AttractorType::RiverMouth, 1.0)]; + let attractors = vec![make_attractor(0, 0, AttractorType::RiverMouth, 100)]; let matrix = uniform_matrix(); let placements = match_cities(&cities, &attractors, &matrix, None, 512, 256); assert_eq!(placements.len(), 2); @@ -555,8 +567,8 @@ mod tests { .map(|i| make_city(i, SettlementClass::PopulationBudget, 100_000)) .collect(); let attractors = vec![ - make_attractor(10, 10, AttractorType::RiverMouth, 0.9), - make_attractor(20, 20, AttractorType::CoastalAccess, 0.7), + make_attractor(10, 10, AttractorType::RiverMouth, 90), + make_attractor(20, 20, AttractorType::CoastalAccess, 70), ]; let matrix = uniform_matrix(); let placements = match_cities(&cities, &attractors, &matrix, None, 512, 256); @@ -568,9 +580,9 @@ mod tests { // 2 cities, 2 attractors. City A scores best on attractor 0, city B best on attractor 1. let mut matrix = uniform_matrix(); // agricultural (row 2) scores high on ValleyFloor (col 3) = 3.0 - matrix.weights[2][3] = 3.0; + matrix.weights[2][3] = 100; // transit_hub (row 6) scores high on RiverCrossing (col 2) = 3.0 - matrix.weights[6][2] = 3.0; + matrix.weights[6][2] = 100; let cities = vec![ CityRecord { city_id: 1, @@ -588,8 +600,8 @@ mod tests { }, ]; let attractors = vec![ - make_attractor(5, 5, AttractorType::ValleyFloor, 1.0), - make_attractor(10, 10, AttractorType::RiverCrossing, 1.0), + make_attractor(5, 5, AttractorType::ValleyFloor, 100), + make_attractor(10, 10, AttractorType::RiverCrossing, 100), ]; let placements = match_cities(&cities, &attractors, &matrix, None, 512, 256); assert_eq!(placements.len(), 2); diff --git a/server/src/atlas/body_world_state.rs b/server/src/atlas/body_world_state.rs index 8f6d1deef..4a4ec87c7 100644 --- a/server/src/atlas/body_world_state.rs +++ b/server/src/atlas/body_world_state.rs @@ -13,6 +13,7 @@ use std::collections::{BTreeMap, BTreeSet}; use bevy_ecs::prelude::Resource; use serde::{Deserialize, Serialize}; +use crate::atlas::attractor_matching::CityPlacement; use crate::simulation::generator::{DistrictId, DistrictWorldState, GeographicAttractor}; /// Simulation tick counter — monotonically increasing u64. @@ -71,6 +72,9 @@ pub struct BodyWorldState { pub drainage_basins: Vec, /// Geographic attractors (D-195, D-209). Empty until attractor task completes. pub attractors: Vec, + /// Settlement placements (D-211, #955). Attractor-matched city positions. + /// Empty until the Layer-3 placement task completes. + pub placements: Vec, /// District-level world state, keyed by `DistrictId` (D-230). /// /// Populated by `GenCompletion::SkeletonGenerated` after the plan phase @@ -196,6 +200,7 @@ mod tests { river_network: RiverNetwork::default(), drainage_basins: vec![], attractors: vec![], + placements: vec![], districts: BTreeMap::new(), last_accessed: tick, } diff --git a/server/src/atlas/cascade.rs b/server/src/atlas/cascade.rs index 17d8a31f6..53cbb6a1f 100644 --- a/server/src/atlas/cascade.rs +++ b/server/src/atlas/cascade.rs @@ -19,10 +19,12 @@ use std::path::Path; use serde::{Deserialize, Serialize}; +use crate::atlas::attractor_matching::{match_cities, CityPlacement, CityRecord}; use crate::atlas::body_world_state::{BodyWorldState, RiverNetwork}; use crate::atlas::heightmap::{self, BodyHeightmap, HeightmapLoadError}; use crate::atlas::layer1::{self, Layer1Output}; use crate::seed::SeedChain; +use crate::simulation::generator::{CompatibilityMatrix, GeographicAttractor}; /// Cascade layers in execution order (D-200). [`run_cascade`] runs every layer /// up to and including the requested one. Append new layers as they are built; @@ -33,6 +35,9 @@ pub enum CascadeLayer { Heightmap, /// Layer 1 — empty-world topography: drainage, feature tags, sub-biome (#953). Topography, + /// Layer 3 — settlement placement: attractor-matched city positions (#955, D-211). + /// First RNG-using layer (uses the carried `SeedChain`). + Settlement, } /// Output of the cascade for one body, up to the requested layer (#952). @@ -49,6 +54,15 @@ pub struct CascadeSnapshot { pub heightmap: BodyHeightmap, /// Layer 1 — topography. `Some` once [`CascadeLayer::Topography`] has run. pub layer1: Option, + /// Layer 3 — settlement placement. `Some` once [`CascadeLayer::Settlement`] has run. + pub layer3: Option, +} + +/// Layer 3 output (#955, D-211): attractor-matched settlement placements for the +/// body. Re-derivable from (Layer-1 attractors + settlement records + seed). +#[derive(Debug, Clone, Default)] +pub struct Layer3Output { + pub placements: Vec, } impl CascadeSnapshot { @@ -61,6 +75,7 @@ impl CascadeSnapshot { Some(l1) => (l1.river_network, l1.drainage_basins, l1.attractors), None => (RiverNetwork::default(), Vec::new(), Vec::new()), }; + let placements = self.layer3.map(|l3| l3.placements).unwrap_or_default(); BodyWorldState { body_id: self.body_id, heightmap: self.heightmap.data, @@ -69,20 +84,42 @@ impl CascadeSnapshot { river_network, drainage_basins, attractors, + placements, districts: std::collections::BTreeMap::new(), last_accessed: 0, } } } +/// Layer 3 — settlement placement (#955, D-211). Pure: matches the body's +/// settlements to its Layer-1 attractors via the authored D-195 compatibility +/// matrix (the five-phase `match_cities` pipeline). Deterministic — a pure +/// function of (attractors, cities); no RNG. +/// +/// `terrain_costs` is `None` for now (uniform 1.0); wiring sub-biome +/// `terrain_modification_cost` (D-234) is a follow-on refinement. +fn run_layer3( + attractors: &[GeographicAttractor], + cities: &[CityRecord], + grid_w: u32, + grid_h: u32, +) -> Layer3Output { + let matrix = CompatibilityMatrix::d195(); + let placements = match_cities(cities, attractors, &matrix, None, grid_w, grid_h); + Layer3Output { placements } +} + /// Run the cascade from a heightmap already in memory, up to `up_to`. /// /// Pure (no I/O); this is the testable core. `body_seed` is this body's /// [`SeedChain`] position — the caller derives it from the world seed via -/// `SeedChain::root(world_seed).derive(SeedDomain::Body, id)`. +/// `SeedChain::root(world_seed).derive(SeedDomain::Body, id)`. `cities` are the +/// body's settlements (from `atlas_city_names`, supplied by the caller — the +/// cascade stays DB-free); empty until Layer 3 (`Settlement`) is requested. pub fn run_cascade_from_heightmap( body_seed: SeedChain, heightmap: BodyHeightmap, + cities: &[CityRecord], up_to: CascadeLayer, ) -> CascadeSnapshot { let mut snapshot = CascadeSnapshot { @@ -90,6 +127,7 @@ pub fn run_cascade_from_heightmap( seed: body_seed, heightmap, layer1: None, + layer3: None, }; // Layer 1 — topography (RNG-free; pure function of the heightmap). @@ -97,6 +135,25 @@ pub fn run_cascade_from_heightmap( snapshot.layer1 = Some(layer1::run_layer1(&snapshot.heightmap)); } + // Layer 3 — settlement placement (D-211). Requires Layer 1 attractors, which + // are present because Settlement > Topography in the layer order. + if up_to >= CascadeLayer::Settlement { + let attractors: &[GeographicAttractor] = match snapshot.layer1.as_ref() { + Some(l1) => &l1.attractors, + None => &[], + }; + // cache seam: run_layer3 is a pure, deterministic function of + // (attractors, cities) — wrap a persistent cache here when we add one + // (build-time bake or local cache; see #1021). + let l3 = run_layer3( + attractors, + cities, + snapshot.heightmap.width, + snapshot.heightmap.height, + ); + snapshot.layer3 = Some(l3); + } + snapshot } @@ -109,11 +166,14 @@ pub fn run_cascade( body_id: &str, heightmap_path: &Path, default_sea_level: f32, + cities: &[CityRecord], up_to: CascadeLayer, ) -> Result { // Layer 0 — the cascade's input; always loaded. let heightmap = heightmap::load_heightmap_png(heightmap_path, body_id, default_sea_level)?; - Ok(run_cascade_from_heightmap(body_seed, heightmap, up_to)) + Ok(run_cascade_from_heightmap( + body_seed, heightmap, cities, up_to, + )) } #[cfg(test)] @@ -149,7 +209,7 @@ mod tests { #[test] fn heightmap_layer_skips_layer1() { let snap = - run_cascade_from_heightmap(body_seed(), test_heightmap(), CascadeLayer::Heightmap); + run_cascade_from_heightmap(body_seed(), test_heightmap(), &[], CascadeLayer::Heightmap); assert_eq!(snap.body_id, "test_body"); assert!( snap.layer1.is_none(), @@ -159,8 +219,12 @@ mod tests { #[test] fn topography_layer_runs_layer1() { - let snap = - run_cascade_from_heightmap(body_seed(), test_heightmap(), CascadeLayer::Topography); + let snap = run_cascade_from_heightmap( + body_seed(), + test_heightmap(), + &[], + CascadeLayer::Topography, + ); let l1 = snap.layer1.expect("Layer 1 should have run"); assert_eq!(l1.body_id, "test_body"); } @@ -177,11 +241,13 @@ mod tests { let a = extract(run_cascade_from_heightmap( body_seed(), test_heightmap(), + &[], CascadeLayer::Topography, )); let b = extract(run_cascade_from_heightmap( body_seed(), test_heightmap(), + &[], CascadeLayer::Topography, )); assert_eq!( @@ -203,8 +269,67 @@ mod tests { "missing", std::path::Path::new("/nonexistent/sr-test/heightmap.png"), 0.3, + &[], CascadeLayer::Heightmap, ); assert!(res.is_err(), "missing heightmap must Err, not panic"); } + + /// Layer 3 — settlement placement runs, places the body's settlements onto + /// attractors, and is deterministic (#955, D-211). + #[test] + fn settlement_layer_places_cities_deterministically() { + use crate::atlas::attractor_matching::CityRecord; + use crate::simulation::generator::SettlementClass; + + let cities = vec![ + CityRecord { + city_id: 1, + name: "Capital".into(), + settlement_class: SettlementClass::NameLocked, + population: 2_000_000, + economic_role: "financial".into(), + }, + CityRecord { + city_id: 2, + name: "Farm Town".into(), + settlement_class: SettlementClass::OrganicGrowth, + population: 120_000, + economic_role: "agricultural".into(), + }, + ]; + let run = || { + run_cascade_from_heightmap( + body_seed(), + test_heightmap(), + &cities, + CascadeLayer::Settlement, + ) + }; + let snap = run(); + let placement_count = { + let l3 = snap.layer3.as_ref().expect("Layer 3 should have run"); + assert!( + !l3.placements.is_empty(), + "settlements must be placed when Layer 1 produced attractors" + ); + l3.placements.len() + }; + // Determinism: same inputs → identical placements (positions + city_ids). + let key = |s: &CascadeSnapshot| { + s.layer3 + .as_ref() + .unwrap() + .placements + .iter() + .map(|p| (p.city_id, p.position, p.attractor_type, p.synthetic)) + .collect::>() + }; + assert_eq!(key(&snap), key(&run()), "placement must be deterministic"); + // The placements propagate into the hot-cache BodyWorldState. + assert_eq!( + snap.into_body_world_state().placements.len(), + placement_count + ); + } } diff --git a/server/src/atlas/features.rs b/server/src/atlas/features.rs index 7c2ce06b3..809f58397 100644 --- a/server/src/atlas/features.rs +++ b/server/src/atlas/features.rs @@ -50,7 +50,10 @@ pub struct RawAttractor { pub row: u16, pub col: u16, pub attractor_type: AttractorType, - pub strength: f32, + /// Strength on a 0–100 integer scale (100 = strongest). Quantized here from + /// the f32 flow-accumulation ratio — the single f32→integer boundary, after + /// which every ranking/scoring decision is integer (D-010, #955). + pub strength: i32, } /// Precomputed per-cell terrain fields, shared by feature extraction (D-209) @@ -311,7 +314,8 @@ pub fn extract_attractors( row: r as u16, col: c as u16, attractor_type: at, - strength: strength.clamp(0.0, 1.0), + // The single f32→integer boundary: quantize the 0.0–1.0 ratio to 0–100. + strength: (strength.clamp(0.0, 1.0) * 100.0).round() as i32, }); }; @@ -462,9 +466,11 @@ pub fn extract_attractors( } for group in by_type.values_mut() { group.sort_by(|a, b| { - let sa = (a.strength * 1e6) as i64; - let sb = (b.strength * 1e6) as i64; - sb.cmp(&sa).then(a.row.cmp(&b.row)).then(a.col.cmp(&b.col)) + // strength is integer now — rank by it directly (descending). + b.strength + .cmp(&a.strength) + .then(a.row.cmp(&b.row)) + .then(a.col.cmp(&b.col)) }); } let mut kept: Vec = Vec::with_capacity(MAX_ATTRACTORS); diff --git a/server/src/atlas/gen_queue.rs b/server/src/atlas/gen_queue.rs index 336c4d4a0..6d7baefa3 100644 --- a/server/src/atlas/gen_queue.rs +++ b/server/src/atlas/gen_queue.rs @@ -356,8 +356,10 @@ fn run_work_item(item: &GenWorkItem) -> GenCompletion { } else { hm }; + // Cities (&[]) get supplied once the Layer-3 settlement read is + // wired through the work item (#955 follow-on); Topography needs none. let snapshot = - run_cascade_from_heightmap(*body_seed, working, CascadeLayer::Topography); + run_cascade_from_heightmap(*body_seed, working, &[], CascadeLayer::Topography); GenCompletion::BodyAnalyzed { body_id: body_id.clone(), state: snapshot.into_body_world_state(), diff --git a/server/src/atlas/layer1.rs b/server/src/atlas/layer1.rs index f8d91b344..ef50e2fef 100644 --- a/server/src/atlas/layer1.rs +++ b/server/src/atlas/layer1.rs @@ -90,9 +90,9 @@ pub fn attach_feature_names( .filter(|a| a.attractor_type == AttractorType::RiverMouth) .collect(); mouths.sort_by(|a, b| { - let sa = (a.strength * 1e6) as i64; - let sb = (b.strength * 1e6) as i64; - sb.cmp(&sa) + // strength is integer now — rank directly (descending). + b.strength + .cmp(&a.strength) .then(a.position.0.cmp(&b.position.0)) .then(a.position.1.cmp(&b.position.1)) }); @@ -114,9 +114,9 @@ pub fn attach_feature_names( }) .collect(); peaks.sort_by(|a, b| { - let sa = (a.strength * 1e6) as i64; - let sb = (b.strength * 1e6) as i64; - sb.cmp(&sa) + // strength is integer now — rank directly (descending). + b.strength + .cmp(&a.strength) .then(a.position.0.cmp(&b.position.0)) .then(a.position.1.cmp(&b.position.1)) }); @@ -163,12 +163,9 @@ mod tests { for (a, b) in o1.attractors.iter().zip(o2.attractors.iter()) { assert_eq!(a.position, b.position); assert_eq!(a.attractor_type, b.attractor_type); - assert_eq!(a.strength.to_bits(), b.strength.to_bits()); + assert_eq!(a.strength, b.strength); assert_eq!(a.sub_biome, b.sub_biome); - assert_eq!( - a.terrain_modification_cost.to_bits(), - b.terrain_modification_cost.to_bits() - ); + assert_eq!(a.terrain_modification_cost, b.terrain_modification_cost); } assert_eq!(o1.river_network.river_cells, o2.river_network.river_cells); } @@ -180,11 +177,8 @@ mod tests { assert!(o .attractors .iter() - .all(|a| a.terrain_modification_cost >= 1.0)); - assert!(o - .attractors - .iter() - .all(|a| (0.0..=1.0).contains(&a.strength))); + .all(|a| a.terrain_modification_cost >= 100)); + assert!(o.attractors.iter().all(|a| (0..=100).contains(&a.strength))); } #[test] diff --git a/server/src/atlas/layer_proxy.rs b/server/src/atlas/layer_proxy.rs index 9ae624a42..8cd30127f 100644 --- a/server/src/atlas/layer_proxy.rs +++ b/server/src/atlas/layer_proxy.rs @@ -200,6 +200,7 @@ mod tests { river_network: RiverNetwork::default(), drainage_basins: vec![], attractors: vec![], + placements: vec![], districts: std::collections::BTreeMap::new(), last_accessed: 0, }); diff --git a/server/src/atlas/subbiome.rs b/server/src/atlas/subbiome.rs index 036bb0722..df467ce60 100644 --- a/server/src/atlas/subbiome.rs +++ b/server/src/atlas/subbiome.rs @@ -32,25 +32,27 @@ fn temperature(row: usize, h: usize) -> f32 { /// Base infrastructure-build cost per sub-biome (D-210 anchors: grassland 1.0, /// coastal lowland 1.4, wetland 3.2, alpine 3.8, volcanic 4.5; the rest /// interpolated by buildability). -fn base_cost(v: SubBiomeVariant) -> f32 { +/// Base build cost as a percent of baseline (100 = 1.0× grassland). Integer for +/// D-010 determinism (#955). +fn base_cost(v: SubBiomeVariant) -> i32 { match v { - SubBiomeVariant::TemperateGrassland => 1.0, - SubBiomeVariant::Savanna => 1.1, - SubBiomeVariant::Desert => 1.2, - SubBiomeVariant::TemperateForest => 1.3, - SubBiomeVariant::CoastalLowland => 1.4, - SubBiomeVariant::BorealForest => 1.5, - SubBiomeVariant::Tundra => 1.6, - SubBiomeVariant::TropicalWet => 2.0, - SubBiomeVariant::Wetland => 3.2, - SubBiomeVariant::Alpine => 3.8, - SubBiomeVariant::Volcanic => 4.5, + SubBiomeVariant::TemperateGrassland => 100, + SubBiomeVariant::Savanna => 110, + SubBiomeVariant::Desert => 120, + SubBiomeVariant::TemperateForest => 130, + SubBiomeVariant::CoastalLowland => 140, + SubBiomeVariant::BorealForest => 150, + SubBiomeVariant::Tundra => 160, + SubBiomeVariant::TropicalWet => 200, + SubBiomeVariant::Wetland => 320, + SubBiomeVariant::Alpine => 380, + SubBiomeVariant::Volcanic => 450, } } /// Classify the sub-biome and compute `terrain_modification_cost` for the cell /// at `(row, col)`. Returns `(variant, cost)`. -pub fn classify(ta: &TerrainAnalysis, row: usize, col: usize) -> (SubBiomeVariant, f32) { +pub fn classify(ta: &TerrainAnalysis, row: usize, col: usize) -> (SubBiomeVariant, i32) { let i = row * ta.w + col; let elev_pct = ta.elev_pct[i]; let slope = ta.slope_deg[i]; @@ -61,7 +63,9 @@ pub fn classify(ta: &TerrainAnalysis, row: usize, col: usize) -> (SubBiomeVarian // Cost = sub-biome base + a slope surcharge (steeper terrain costs more to // build on), capped so a steep grassland never out-costs flat volcanic. - let slope_surcharge = (slope / 12.0).min(1.5); + // Surcharge in percent points (0–150): the f32 slope is quantized here, the + // single f32→integer boundary; the cost itself is integer (D-010, #955). + let slope_surcharge = ((slope / 12.0).min(1.5) * 100.0).round() as i32; let cost = base_cost(variant) + slope_surcharge; (variant, cost) @@ -139,7 +143,7 @@ mod tests { base_cost(SubBiomeVariant::Alpine), ); assert_eq!(v, SubBiomeVariant::Alpine); - assert!(cost > 3.0); + assert!(cost > 300, "alpine cost well above the 100 baseline"); } #[test] @@ -156,8 +160,8 @@ mod tests { let (v1, c1) = classify(&ta, 10, 20); let (v2, c2) = classify(&ta, 10, 20); assert_eq!(v1, v2); - assert_eq!(c1.to_bits(), c2.to_bits()); - assert!(c1 >= 1.0, "cost is at least the grassland baseline"); + assert_eq!(c1, c2); + assert!(c1 >= 100, "cost is at least the grassland baseline"); } #[test] diff --git a/server/src/simulation/generator.rs b/server/src/simulation/generator.rs index 0a68fc7e2..9c9e95a8f 100644 --- a/server/src/simulation/generator.rs +++ b/server/src/simulation/generator.rs @@ -439,27 +439,62 @@ pub struct GeographicAttractor { /// Pixel position in heightmap space [row, col]. pub position: (u16, u16), pub attractor_type: AttractorType, - /// Normalized strength 0.0–1.0. Derived from flow accumulation or habitability score. - pub strength: f32, + /// Strength on a 0–100 integer scale (100 = strongest). Quantized from flow + /// accumulation / habitability at Layer-1 output. Integer for D-010 + /// determinism — every ranking/scoring decision is exact, no f32 (#955). + pub strength: i32, /// Fine-grained terrain classification at this position (D-210). pub sub_biome: SubBiomeVariant, - /// Infrastructure-build cost multiplier (1.0 = baseline grassland; higher = - /// more expensive). Derived from `sub_biome` + local slope. Consumed by the - /// attractor-matching pipeline (D-211) to penalize marginal cities. (D-210) - pub terrain_modification_cost: f32, + /// Infrastructure-build cost as a percent of baseline (100 = baseline + /// grassland; >100 = more expensive, e.g. 200 = 2× cost). Derived from + /// `sub_biome` + local slope; penalizes marginal cities in the matching + /// pipeline (D-211). Integer for D-010 determinism (#955). (D-210) + pub terrain_modification_cost: i32, } /// Compatibility weights between economic roles and attractor types. /// A 10×7 matrix (10 economic_role values × 7 AttractorType variants). -/// Each cell is a weight multiplier 0.0–3.0 applied during attractor-matching scoring. -/// Source: D-195 +/// Each cell is an integer affinity 0–100 (100 = ideal terrain for that role, +/// ~50 = neutral, low = poor) used during attractor-matching scoring. +/// Integer for D-010 determinism (#955). Source: D-195 #[derive(Serialize, Deserialize, Clone, Debug)] pub struct CompatibilityMatrix { /// Row order: manufacturing, financial, agricultural, extraction, service_mixed, /// institutional, transit_hub, research, military, residential. /// Column order: RiverMouth, CoastalAccess, RiverCrossing, ValleyFloor, /// PassEntrance, LakeShore, PlainCenter. - pub weights: [[f32; 7]; 10], + pub weights: [[i32; 7]; 10], +} + +impl CompatibilityMatrix { + /// The canonical D-195 authored compatibility table (#955). + /// + /// Integer affinity 0–100: 100 = ideal terrain for the role, ~50 = neutral, + /// low (10–30) = poor fit (graduated, never a hard 0 — D-195). Re-authored + /// on the 0–100 scale from D-195's relative examples (the agricultural→ + /// ValleyFloor 3.0 peak maps to the global max 100). + /// + /// Rows: manufacturing, financial, agricultural, extraction, service_mixed, + /// institutional, transit_hub, research, military, residential. + /// Cols: RiverMouth, CoastalAccess, RiverCrossing, ValleyFloor, + /// PassEntrance, LakeShore, PlainCenter. + pub fn d195() -> Self { + CompatibilityMatrix { + weights: [ + // RMth Coast RXing Vall Pass Lake Plain + [95, 80, 75, 60, 30, 35, 55], // manufacturing + [70, 90, 65, 25, 40, 45, 70], // financial + [85, 30, 55, 100, 10, 70, 90], // agricultural + [30, 35, 40, 65, 95, 30, 25], // extraction + [60, 65, 70, 50, 35, 50, 70], // service_mixed + [65, 65, 60, 45, 20, 55, 90], // institutional + [65, 75, 100, 35, 95, 25, 60], // transit_hub + [40, 70, 35, 75, 40, 95, 60], // research + [60, 75, 80, 55, 100, 25, 40], // military + [70, 85, 50, 65, 20, 85, 70], // residential + ], + } + } } // --------------------------------------------------------------------------- diff --git a/server/tests/cascade_golden.rs b/server/tests/cascade_golden.rs index a2dbd66fb..324626864 100644 --- a/server/tests/cascade_golden.rs +++ b/server/tests/cascade_golden.rs @@ -55,7 +55,7 @@ fn cascade_layer0_to_1_matches_golden() { // ── Layer 1 — downsample, then run the cascade to topography. ─────────── let small = heightmap.downsample(DOWNSAMPLE.0, DOWNSAMPLE.1); let body_seed = SeedChain::for_body(WORLD_SEED, "GJ1c"); - let snapshot = run_cascade_from_heightmap(body_seed, small, CascadeLayer::Topography); + let snapshot = run_cascade_from_heightmap(body_seed, small, &[], CascadeLayer::Topography); let layer1 = snapshot.layer1.expect("Layer 1 ran"); let actual = json!({ diff --git a/server/tests/gen_fixtures.rs b/server/tests/gen_fixtures.rs index 804c1220a..60c8b0f14 100644 --- a/server/tests/gen_fixtures.rs +++ b/server/tests/gen_fixtures.rs @@ -557,9 +557,9 @@ fn generate_atlas_layer_response_fixtures() { attractors: vec![GeographicAttractor { position: (12, 58), attractor_type: AttractorType::CoastalAccess, - strength: 0.9, + strength: 90, sub_biome: SubBiomeVariant::CoastalLowland, - terrain_modification_cost: 1.7, + terrain_modification_cost: 170, }], grid_w: 512, grid_h: 256, diff --git a/server/tests/golden/cascade_layer1.json b/server/tests/golden/cascade_layer1.json index 99541ef19..f9545d732 100644 --- a/server/tests/golden/cascade_layer1.json +++ b/server/tests/golden/cascade_layer1.json @@ -11,9 +11,9 @@ 0, 100 ], - "strength": 0.1970033347606659, + "strength": 20, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6161832809448242 + "terrain_modification_cost": 162 }, { "attractor_type": "RiverMouth", @@ -21,9 +21,9 @@ 0, 142 ], - "strength": 0.13817979395389557, + "strength": 14, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6532286405563354 + "terrain_modification_cost": 165 }, { "attractor_type": "RiverMouth", @@ -31,9 +31,9 @@ 0, 197 ], - "strength": 0.1348501592874527, + "strength": 13, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6613280773162842 + "terrain_modification_cost": 166 }, { "attractor_type": "RiverMouth", @@ -41,9 +41,9 @@ 0, 241 ], - "strength": 0.22863484919071198, + "strength": 23, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6579999923706055 + "terrain_modification_cost": 166 }, { "attractor_type": "RiverMouth", @@ -51,9 +51,9 @@ 11, 65 ], - "strength": 0.12097669392824173, + "strength": 12, "sub_biome": "Alpine", - "terrain_modification_cost": 3.842979669570923 + "terrain_modification_cost": 384 }, { "attractor_type": "RiverMouth", @@ -61,9 +61,9 @@ 12, 232 ], - "strength": 0.15371808409690857, + "strength": 15, "sub_biome": "Tundra", - "terrain_modification_cost": 1.655318260192871 + "terrain_modification_cost": 166 }, { "attractor_type": "RiverMouth", @@ -71,9 +71,9 @@ 12, 238 ], - "strength": 0.23362930119037628, + "strength": 23, "sub_biome": "Tundra", - "terrain_modification_cost": 1.626688003540039 + "terrain_modification_cost": 163 }, { "attractor_type": "RiverMouth", @@ -81,9 +81,9 @@ 15, 141 ], - "strength": 0.22475028038024902, + "strength": 22, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4988292455673218 + "terrain_modification_cost": 150 }, { "attractor_type": "RiverMouth", @@ -91,9 +91,9 @@ 20, 85 ], - "strength": 0.23973363637924194, + "strength": 24, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.5785413980484009 + "terrain_modification_cost": 158 }, { "attractor_type": "RiverMouth", @@ -101,9 +101,9 @@ 25, 100 ], - "strength": 0.21587125957012177, + "strength": 22, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4586601257324219 + "terrain_modification_cost": 146 }, { "attractor_type": "RiverMouth", @@ -111,9 +111,9 @@ 28, 14 ], - "strength": 0.3390676975250244, + "strength": 34, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.500869631767273 + "terrain_modification_cost": 150 }, { "attractor_type": "RiverMouth", @@ -121,9 +121,9 @@ 28, 181 ], - "strength": 0.19755826890468597, + "strength": 20, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2738523483276367 + "terrain_modification_cost": 327 }, { "attractor_type": "RiverMouth", @@ -131,9 +131,9 @@ 38, 47 ], - "strength": 0.15982241928577423, + "strength": 16, "sub_biome": "Wetland", - "terrain_modification_cost": 3.289930582046509 + "terrain_modification_cost": 329 }, { "attractor_type": "RiverMouth", @@ -141,9 +141,9 @@ 38, 98 ], - "strength": 0.12985572218894958, + "strength": 13, "sub_biome": "Wetland", - "terrain_modification_cost": 3.4823851585388184 + "terrain_modification_cost": 348 }, { "attractor_type": "RiverMouth", @@ -151,9 +151,9 @@ 50, 226 ], - "strength": 0.12819090485572815, + "strength": 13, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2536017894744873 + "terrain_modification_cost": 325 }, { "attractor_type": "RiverMouth", @@ -161,9 +161,9 @@ 50, 254 ], - "strength": 0.15982241928577423, + "strength": 16, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2409305572509766 + "terrain_modification_cost": 324 }, { "attractor_type": "RiverMouth", @@ -171,9 +171,9 @@ 51, 230 ], - "strength": 0.24750277400016785, + "strength": 25, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2201220989227295 + "terrain_modification_cost": 322 }, { "attractor_type": "RiverMouth", @@ -181,9 +181,9 @@ 88, 217 ], - "strength": 0.12541620433330536, + "strength": 13, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4192293882369995 + "terrain_modification_cost": 142 }, { "attractor_type": "RiverMouth", @@ -191,9 +191,9 @@ 124, 239 ], - "strength": 0.11764705926179886, + "strength": 12, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2694363594055176 + "terrain_modification_cost": 327 }, { "attractor_type": "CoastalAccess", @@ -201,9 +201,9 @@ 25, 117 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5674556493759155 + "terrain_modification_cost": 157 }, { "attractor_type": "CoastalAccess", @@ -211,9 +211,9 @@ 25, 129 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5940327644348145 + "terrain_modification_cost": 159 }, { "attractor_type": "CoastalAccess", @@ -221,9 +221,9 @@ 32, 101 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3026435375213623 + "terrain_modification_cost": 330 }, { "attractor_type": "CoastalAccess", @@ -231,9 +231,9 @@ 35, 50 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.360719919204712 + "terrain_modification_cost": 336 }, { "attractor_type": "CoastalAccess", @@ -241,9 +241,9 @@ 35, 148 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.4359264373779297 + "terrain_modification_cost": 344 }, { "attractor_type": "CoastalAccess", @@ -251,9 +251,9 @@ 36, 8 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.302083730697632 + "terrain_modification_cost": 330 }, { "attractor_type": "CoastalAccess", @@ -261,9 +261,9 @@ 36, 182 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.33066725730896 + "terrain_modification_cost": 333 }, { "attractor_type": "CoastalAccess", @@ -271,9 +271,9 @@ 36, 198 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3325600624084473 + "terrain_modification_cost": 333 }, { "attractor_type": "CoastalAccess", @@ -281,9 +281,9 @@ 37, 126 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.310631036758423 + "terrain_modification_cost": 331 }, { "attractor_type": "CoastalAccess", @@ -291,9 +291,9 @@ 38, 62 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.252418279647827 + "terrain_modification_cost": 325 }, { "attractor_type": "CoastalAccess", @@ -301,9 +301,9 @@ 38, 167 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3096070289611816 + "terrain_modification_cost": 331 }, { "attractor_type": "CoastalAccess", @@ -311,9 +311,9 @@ 41, 89 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.9096131324768066 + "terrain_modification_cost": 191 }, { "attractor_type": "CoastalAccess", @@ -321,9 +321,9 @@ 41, 113 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2514119148254395 + "terrain_modification_cost": 325 }, { "attractor_type": "CoastalAccess", @@ -331,9 +331,9 @@ 42, 210 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.28499174118042 + "terrain_modification_cost": 328 }, { "attractor_type": "CoastalAccess", @@ -341,9 +341,9 @@ 44, 101 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.7026764154434204 + "terrain_modification_cost": 170 }, { "attractor_type": "CoastalAccess", @@ -351,9 +351,9 @@ 46, 20 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.296772003173828 + "terrain_modification_cost": 330 }, { "attractor_type": "CoastalAccess", @@ -361,9 +361,9 @@ 47, 77 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2567570209503174 + "terrain_modification_cost": 326 }, { "attractor_type": "CoastalAccess", @@ -371,9 +371,9 @@ 48, 2 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.277508020401001 + "terrain_modification_cost": 328 }, { "attractor_type": "CoastalAccess", @@ -381,9 +381,9 @@ 48, 182 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3120782375335693 + "terrain_modification_cost": 331 }, { "attractor_type": "CoastalAccess", @@ -391,9 +391,9 @@ 49, 127 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.215940237045288 + "terrain_modification_cost": 222 }, { "attractor_type": "CoastalAccess", @@ -401,9 +401,9 @@ 50, 58 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.303185224533081 + "terrain_modification_cost": 330 }, { "attractor_type": "CoastalAccess", @@ -411,9 +411,9 @@ 50, 157 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2933719158172607 + "terrain_modification_cost": 329 }, { "attractor_type": "CoastalAccess", @@ -421,9 +421,9 @@ 51, 194 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2475645542144775 + "terrain_modification_cost": 325 }, { "attractor_type": "CoastalAccess", @@ -431,9 +431,9 @@ 52, 139 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.287991523742676 + "terrain_modification_cost": 329 }, { "attractor_type": "CoastalAccess", @@ -441,9 +441,9 @@ 52, 255 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.246562957763672 + "terrain_modification_cost": 325 }, { "attractor_type": "CoastalAccess", @@ -451,9 +451,9 @@ 54, 215 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.961440324783325 + "terrain_modification_cost": 396 }, { "attractor_type": "CoastalAccess", @@ -461,9 +461,9 @@ 55, 113 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.6665822267532349 + "terrain_modification_cost": 167 }, { "attractor_type": "CoastalAccess", @@ -471,9 +471,9 @@ 58, 27 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.249171733856201 + "terrain_modification_cost": 325 }, { "attractor_type": "CoastalAccess", @@ -481,9 +481,9 @@ 60, 6 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2986154556274414 + "terrain_modification_cost": 330 }, { "attractor_type": "CoastalAccess", @@ -491,9 +491,9 @@ 62, 66 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.648212194442749 + "terrain_modification_cost": 165 }, { "attractor_type": "CoastalAccess", @@ -501,9 +501,9 @@ 63, 39 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.23246169090271 + "terrain_modification_cost": 323 }, { "attractor_type": "CoastalAccess", @@ -511,9 +511,9 @@ 63, 191 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.5751609802246094 + "terrain_modification_cost": 358 }, { "attractor_type": "CoastalAccess", @@ -521,9 +521,9 @@ 64, 145 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.385981321334839 + "terrain_modification_cost": 339 }, { "attractor_type": "CoastalAccess", @@ -531,9 +531,9 @@ 64, 252 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.5039875507354736 + "terrain_modification_cost": 350 }, { "attractor_type": "CoastalAccess", @@ -541,9 +541,9 @@ 65, 203 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "TropicalWet", - "terrain_modification_cost": 3.1433067321777344 + "terrain_modification_cost": 314 }, { "attractor_type": "CoastalAccess", @@ -551,9 +551,9 @@ 67, 123 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3792519569396973 + "terrain_modification_cost": 338 }, { "attractor_type": "CoastalAccess", @@ -561,9 +561,9 @@ 70, 18 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.65916109085083 + "terrain_modification_cost": 266 }, { "attractor_type": "CoastalAccess", @@ -571,9 +571,9 @@ 70, 51 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.7184526920318604 + "terrain_modification_cost": 272 }, { "attractor_type": "CoastalAccess", @@ -581,9 +581,9 @@ 70, 78 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.703115224838257 + "terrain_modification_cost": 270 }, { "attractor_type": "CoastalAccess", @@ -591,9 +591,9 @@ 74, 63 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.509533643722534 + "terrain_modification_cost": 351 }, { "attractor_type": "CoastalAccess", @@ -601,9 +601,9 @@ 76, 35 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2861571311950684 + "terrain_modification_cost": 329 }, { "attractor_type": "CoastalAccess", @@ -611,9 +611,9 @@ 76, 241 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.9247565269470215 + "terrain_modification_cost": 192 }, { "attractor_type": "CoastalAccess", @@ -621,9 +621,9 @@ 77, 202 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.8509974479675293 + "terrain_modification_cost": 385 }, { "attractor_type": "CoastalAccess", @@ -631,9 +631,9 @@ 82, 11 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2797658443450928 + "terrain_modification_cost": 328 }, { "attractor_type": "CoastalAccess", @@ -641,9 +641,9 @@ 82, 50 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3259077072143555 + "terrain_modification_cost": 333 }, { "attractor_type": "CoastalAccess", @@ -651,9 +651,9 @@ 84, 23 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2351162433624268 + "terrain_modification_cost": 324 }, { "attractor_type": "CoastalAccess", @@ -661,9 +661,9 @@ 86, 146 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.296030044555664 + "terrain_modification_cost": 330 }, { "attractor_type": "CoastalAccess", @@ -671,9 +671,9 @@ 88, 38 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.228167772293091 + "terrain_modification_cost": 323 }, { "attractor_type": "CoastalAccess", @@ -681,9 +681,9 @@ 88, 233 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.9380714893341064 + "terrain_modification_cost": 194 }, { "attractor_type": "CoastalAccess", @@ -691,9 +691,9 @@ 89, 202 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.442962169647217 + "terrain_modification_cost": 344 }, { "attractor_type": "CoastalAccess", @@ -701,9 +701,9 @@ 91, 67 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.305875062942505 + "terrain_modification_cost": 331 }, { "attractor_type": "CoastalAccess", @@ -711,9 +711,9 @@ 92, 89 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2170257568359375 + "terrain_modification_cost": 322 }, { "attractor_type": "CoastalAccess", @@ -721,9 +721,9 @@ 94, 221 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.371303081512451 + "terrain_modification_cost": 337 }, { "attractor_type": "CoastalAccess", @@ -731,9 +731,9 @@ 95, 101 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.243530511856079 + "terrain_modification_cost": 324 }, { "attractor_type": "CoastalAccess", @@ -741,9 +741,9 @@ 99, 113 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.22542667388916 + "terrain_modification_cost": 323 }, { "attractor_type": "CoastalAccess", @@ -751,9 +751,9 @@ 102, 209 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2309863567352295 + "terrain_modification_cost": 323 }, { "attractor_type": "CoastalAccess", @@ -761,9 +761,9 @@ 103, 63 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.261209726333618 + "terrain_modification_cost": 326 }, { "attractor_type": "CoastalAccess", @@ -771,9 +771,9 @@ 104, 88 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2111105918884277 + "terrain_modification_cost": 321 }, { "attractor_type": "CoastalAccess", @@ -781,9 +781,9 @@ 105, 197 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2789602279663086 + "terrain_modification_cost": 328 }, { "attractor_type": "CoastalAccess", @@ -791,9 +791,9 @@ 106, 226 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2875728607177734 + "terrain_modification_cost": 329 }, { "attractor_type": "CoastalAccess", @@ -801,9 +801,9 @@ 108, 15 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.4143998622894287 + "terrain_modification_cost": 341 }, { "attractor_type": "CoastalAccess", @@ -811,9 +811,9 @@ 110, 27 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "BorealForest", - "terrain_modification_cost": 2.3134572505950928 + "terrain_modification_cost": 231 }, { "attractor_type": "CoastalAccess", @@ -821,9 +821,9 @@ 111, 39 ], - "strength": 0.9000000357627869, + "strength": 90, "sub_biome": "BorealForest", - "terrain_modification_cost": 2.496652364730835 + "terrain_modification_cost": 250 }, { "attractor_type": "ValleyFloor", @@ -831,9 +831,9 @@ 0, 101 ], - "strength": 0.8718692660331726, + "strength": 87, "sub_biome": "Tundra", - "terrain_modification_cost": 1.62393319606781 + "terrain_modification_cost": 162 }, { "attractor_type": "ValleyFloor", @@ -841,9 +841,9 @@ 0, 141 ], - "strength": 0.885496199131012, + "strength": 89, "sub_biome": "Tundra", - "terrain_modification_cost": 1.641026258468628 + "terrain_modification_cost": 164 }, { "attractor_type": "ValleyFloor", @@ -851,9 +851,9 @@ 0, 198 ], - "strength": 0.8640600442886353, + "strength": 86, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6437582969665527 + "terrain_modification_cost": 164 }, { "attractor_type": "ValleyFloor", @@ -861,9 +861,9 @@ 15, 140 ], - "strength": 0.9373704195022583, + "strength": 94, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.591464638710022 + "terrain_modification_cost": 159 }, { "attractor_type": "ValleyFloor", @@ -871,9 +871,9 @@ 20, 86 ], - "strength": 0.9076009392738342, + "strength": 91, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.543671727180481 + "terrain_modification_cost": 154 }, { "attractor_type": "ValleyFloor", @@ -881,9 +881,9 @@ 23, 182 ], - "strength": 0.8782604932785034, + "strength": 88, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.579465389251709 + "terrain_modification_cost": 158 }, { "attractor_type": "ValleyFloor", @@ -891,9 +891,9 @@ 24, 101 ], - "strength": 0.9762054681777954, + "strength": 98, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4586601257324219 + "terrain_modification_cost": 146 }, { "attractor_type": "ValleyFloor", @@ -901,9 +901,9 @@ 25, 121 ], - "strength": 0.9455870389938354, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5602058172225952 + "terrain_modification_cost": 156 }, { "attractor_type": "ValleyFloor", @@ -911,9 +911,9 @@ 27, 16 ], - "strength": 0.8908358812332153, + "strength": 89, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.3496224880218506 + "terrain_modification_cost": 135 }, { "attractor_type": "ValleyFloor", @@ -921,9 +921,9 @@ 29, 137 ], - "strength": 0.9559986591339111, + "strength": 96, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.433650016784668 + "terrain_modification_cost": 143 }, { "attractor_type": "ValleyFloor", @@ -931,9 +931,9 @@ 32, 156 ], - "strength": 0.9065600633621216, + "strength": 91, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5256211757659912 + "terrain_modification_cost": 153 }, { "attractor_type": "ValleyFloor", @@ -941,9 +941,9 @@ 35, 64 ], - "strength": 0.9131754636764526, + "strength": 91, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.6351633071899414 + "terrain_modification_cost": 164 }, { "attractor_type": "ValleyFloor", @@ -951,9 +951,9 @@ 35, 168 ], - "strength": 0.941495418548584, + "strength": 94, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.460085153579712 + "terrain_modification_cost": 146 }, { "attractor_type": "ValleyFloor", @@ -961,9 +961,9 @@ 35, 182 ], - "strength": 0.9487524032592773, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.51590096950531 + "terrain_modification_cost": 152 }, { "attractor_type": "ValleyFloor", @@ -971,9 +971,9 @@ 35, 196 ], - "strength": 0.9326236248016357, + "strength": 93, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5483744144439697 + "terrain_modification_cost": 155 }, { "attractor_type": "ValleyFloor", @@ -981,9 +981,9 @@ 36, 94 ], - "strength": 0.8467390537261963, + "strength": 85, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4571075439453125 + "terrain_modification_cost": 146 }, { "attractor_type": "ValleyFloor", @@ -991,9 +991,9 @@ 40, 213 ], - "strength": 0.9450453519821167, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.493271827697754 + "terrain_modification_cost": 149 }, { "attractor_type": "ValleyFloor", @@ -1001,9 +1001,9 @@ 42, 18 ], - "strength": 0.9442563056945801, + "strength": 94, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4685896635055542 + "terrain_modification_cost": 147 }, { "attractor_type": "ValleyFloor", @@ -1011,9 +1011,9 @@ 42, 30 ], - "strength": 0.9329462647438049, + "strength": 93, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.341235637664795 + "terrain_modification_cost": 134 }, { "attractor_type": "ValleyFloor", @@ -1021,9 +1021,9 @@ 42, 82 ], - "strength": 0.917441725730896, + "strength": 92, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.6187940835952759 + "terrain_modification_cost": 162 }, { "attractor_type": "ValleyFloor", @@ -1031,9 +1031,9 @@ 43, 43 ], - "strength": 0.9623773097991943, + "strength": 96, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.423464059829712 + "terrain_modification_cost": 142 }, { "attractor_type": "ValleyFloor", @@ -1041,9 +1041,9 @@ 45, 1 ], - "strength": 0.9442689418792725, + "strength": 94, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.528292179107666 + "terrain_modification_cost": 153 }, { "attractor_type": "ValleyFloor", @@ -1051,9 +1051,9 @@ 45, 253 ], - "strength": 0.9379899501800537, + "strength": 94, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4756277799606323 + "terrain_modification_cost": 148 }, { "attractor_type": "ValleyFloor", @@ -1061,9 +1061,9 @@ 46, 156 ], - "strength": 0.9526200294494629, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4712119102478027 + "terrain_modification_cost": 147 }, { "attractor_type": "ValleyFloor", @@ -1071,9 +1071,9 @@ 46, 231 ], - "strength": 0.921684980392456, + "strength": 92, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5668507814407349 + "terrain_modification_cost": 157 }, { "attractor_type": "ValleyFloor", @@ -1081,9 +1081,9 @@ 47, 70 ], - "strength": 0.9491028785705566, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4911551475524902 + "terrain_modification_cost": 149 }, { "attractor_type": "ValleyFloor", @@ -1091,9 +1091,9 @@ 48, 110 ], - "strength": 0.9452494382858276, + "strength": 95, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.0404934883117676 + "terrain_modification_cost": 204 }, { "attractor_type": "ValleyFloor", @@ -1101,9 +1101,9 @@ 50, 122 ], - "strength": 0.9450274705886841, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4356671571731567 + "terrain_modification_cost": 144 }, { "attractor_type": "ValleyFloor", @@ -1111,9 +1111,9 @@ 51, 137 ], - "strength": 0.9393389225006104, + "strength": 94, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5606789588928223 + "terrain_modification_cost": 156 }, { "attractor_type": "ValleyFloor", @@ -1121,9 +1121,9 @@ 52, 55 ], - "strength": 0.950191855430603, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4625937938690186 + "terrain_modification_cost": 146 }, { "attractor_type": "ValleyFloor", @@ -1131,9 +1131,9 @@ 58, 228 ], - "strength": 0.9372482895851135, + "strength": 94, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.0334815979003906 + "terrain_modification_cost": 203 }, { "attractor_type": "ValleyFloor", @@ -1141,9 +1141,9 @@ 59, 215 ], - "strength": 0.9184203147888184, + "strength": 92, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.606487512588501 + "terrain_modification_cost": 161 }, { "attractor_type": "ValleyFloor", @@ -1151,9 +1151,9 @@ 62, 32 ], - "strength": 0.8922643661499023, + "strength": 89, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4395874738693237 + "terrain_modification_cost": 144 }, { "attractor_type": "ValleyFloor", @@ -1161,9 +1161,9 @@ 63, 67 ], - "strength": 0.8881745338439941, + "strength": 89, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.7994933128356934 + "terrain_modification_cost": 180 }, { "attractor_type": "ValleyFloor", @@ -1171,9 +1171,9 @@ 64, 194 ], - "strength": 0.8845980167388916, + "strength": 88, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.7359050512313843 + "terrain_modification_cost": 174 }, { "attractor_type": "ValleyFloor", @@ -1181,9 +1181,9 @@ 65, 182 ], - "strength": 0.9251652956008911, + "strength": 93, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5636625289916992 + "terrain_modification_cost": 156 }, { "attractor_type": "ValleyFloor", @@ -1191,9 +1191,9 @@ 66, 2 ], - "strength": 0.8620558977127075, + "strength": 86, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2774670124053955 + "terrain_modification_cost": 328 }, { "attractor_type": "ValleyFloor", @@ -1201,9 +1201,9 @@ 66, 249 ], - "strength": 0.954229474067688, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5275413990020752 + "terrain_modification_cost": 153 }, { "attractor_type": "ValleyFloor", @@ -1211,9 +1211,9 @@ 68, 147 ], - "strength": 0.9325783252716064, + "strength": 93, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2253129482269287 + "terrain_modification_cost": 323 }, { "attractor_type": "ValleyFloor", @@ -1221,9 +1221,9 @@ 72, 54 ], - "strength": 0.920207142829895, + "strength": 92, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5419644117355347 + "terrain_modification_cost": 154 }, { "attractor_type": "ValleyFloor", @@ -1231,9 +1231,9 @@ 75, 23 ], - "strength": 0.9393610954284668, + "strength": 94, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4801572561264038 + "terrain_modification_cost": 148 }, { "attractor_type": "ValleyFloor", @@ -1241,9 +1241,9 @@ 75, 66 ], - "strength": 0.9389851093292236, + "strength": 94, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5790153741836548 + "terrain_modification_cost": 158 }, { "attractor_type": "ValleyFloor", @@ -1251,9 +1251,9 @@ 85, 42 ], - "strength": 0.9098596572875977, + "strength": 91, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4563381671905518 + "terrain_modification_cost": 146 }, { "attractor_type": "ValleyFloor", @@ -1261,9 +1261,9 @@ 86, 203 ], - "strength": 0.90484619140625, + "strength": 90, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2791695594787598 + "terrain_modification_cost": 328 }, { "attractor_type": "ValleyFloor", @@ -1271,9 +1271,9 @@ 86, 215 ], - "strength": 0.9759268760681152, + "strength": 98, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.435261845588684 + "terrain_modification_cost": 144 }, { "attractor_type": "ValleyFloor", @@ -1281,9 +1281,9 @@ 92, 227 ], - "strength": 0.8636218309402466, + "strength": 86, "sub_biome": "Wetland", - "terrain_modification_cost": 3.4696733951568604 + "terrain_modification_cost": 347 }, { "attractor_type": "ValleyFloor", @@ -1291,9 +1291,9 @@ 107, 213 ], - "strength": 0.9129804372787476, + "strength": 91, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5308538675308228 + "terrain_modification_cost": 153 }, { "attractor_type": "ValleyFloor", @@ -1301,9 +1301,9 @@ 111, 201 ], - "strength": 0.9567747116088867, + "strength": 96, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4509565830230713 + "terrain_modification_cost": 145 }, { "attractor_type": "ValleyFloor", @@ -1311,9 +1311,9 @@ 113, 232 ], - "strength": 0.9542192220687866, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.478268027305603 + "terrain_modification_cost": 148 }, { "attractor_type": "ValleyFloor", @@ -1321,9 +1321,9 @@ 115, 14 ], - "strength": 0.9513620138168335, + "strength": 95, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.494455099105835 + "terrain_modification_cost": 149 }, { "attractor_type": "ValleyFloor", @@ -1331,9 +1331,9 @@ 115, 65 ], - "strength": 0.9346446990966797, + "strength": 93, "sub_biome": "Wetland", - "terrain_modification_cost": 3.270233154296875 + "terrain_modification_cost": 327 }, { "attractor_type": "ValleyFloor", @@ -1341,9 +1341,9 @@ 115, 84 ], - "strength": 0.8668441772460938, + "strength": 87, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.421647310256958 + "terrain_modification_cost": 142 }, { "attractor_type": "ValleyFloor", @@ -1351,9 +1351,9 @@ 117, 100 ], - "strength": 0.9425357580184937, + "strength": 94, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4295201301574707 + "terrain_modification_cost": 143 }, { "attractor_type": "ValleyFloor", @@ -1361,9 +1361,9 @@ 119, 26 ], - "strength": 0.9350091218948364, + "strength": 94, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6472276449203491 + "terrain_modification_cost": 165 }, { "attractor_type": "ValleyFloor", @@ -1371,9 +1371,9 @@ 122, 44 ], - "strength": 0.8810006380081177, + "strength": 88, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.7097374200820923 + "terrain_modification_cost": 171 }, { "attractor_type": "ValleyFloor", @@ -1381,9 +1381,9 @@ 123, 131 ], - "strength": 0.9031271934509277, + "strength": 90, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4218248128890991 + "terrain_modification_cost": 142 }, { "attractor_type": "ValleyFloor", @@ -1391,9 +1391,9 @@ 124, 119 ], - "strength": 0.9612393379211426, + "strength": 96, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4328895807266235 + "terrain_modification_cost": 143 }, { "attractor_type": "ValleyFloor", @@ -1401,9 +1401,9 @@ 127, 66 ], - "strength": 0.8864685297012329, + "strength": 89, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4124811887741089 + "terrain_modification_cost": 141 }, { "attractor_type": "ValleyFloor", @@ -1411,9 +1411,9 @@ 127, 143 ], - "strength": 0.8618813753128052, + "strength": 86, "sub_biome": "Wetland", - "terrain_modification_cost": 3.311650514602661 + "terrain_modification_cost": 331 }, { "attractor_type": "ValleyFloor", @@ -1421,9 +1421,9 @@ 127, 200 ], - "strength": 0.9283623695373535, + "strength": 93, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4549859762191772 + "terrain_modification_cost": 145 }, { "attractor_type": "ValleyFloor", @@ -1431,9 +1431,9 @@ 127, 230 ], - "strength": 0.9248091578483582, + "strength": 92, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6315100193023682 + "terrain_modification_cost": 163 }, { "attractor_type": "ValleyFloor", @@ -1441,9 +1441,9 @@ 127, 250 ], - "strength": 0.8682236671447754, + "strength": 87, "sub_biome": "Wetland", - "terrain_modification_cost": 3.264615058898926 + "terrain_modification_cost": 326 }, { "attractor_type": "PassEntrance", @@ -1451,9 +1451,9 @@ 1, 100 ], - "strength": 0.45673274993896484, + "strength": 46, "sub_biome": "Tundra", - "terrain_modification_cost": 1.61869215965271 + "terrain_modification_cost": 162 }, { "attractor_type": "PassEntrance", @@ -1461,9 +1461,9 @@ 3, 133 ], - "strength": 0.48961377143859863, + "strength": 49, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6333905458450317 + "terrain_modification_cost": 163 }, { "attractor_type": "PassEntrance", @@ -1471,9 +1471,9 @@ 4, 54 ], - "strength": 0.009759902954101562, + "strength": 1, "sub_biome": "Alpine", - "terrain_modification_cost": 3.887768507003784 + "terrain_modification_cost": 389 }, { "attractor_type": "PassEntrance", @@ -1481,9 +1481,9 @@ 7, 195 ], - "strength": 0.3426930904388428, + "strength": 34, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6493721008300781 + "terrain_modification_cost": 165 }, { "attractor_type": "PassEntrance", @@ -1491,9 +1491,9 @@ 10, 69 ], - "strength": 0.12760961055755615, + "strength": 13, "sub_biome": "Alpine", - "terrain_modification_cost": 3.8191473484039307 + "terrain_modification_cost": 382 }, { "attractor_type": "PassEntrance", @@ -1501,9 +1501,9 @@ 11, 249 ], - "strength": 0.28763049840927124, + "strength": 29, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6192795038223267 + "terrain_modification_cost": 162 }, { "attractor_type": "PassEntrance", @@ -1511,9 +1511,9 @@ 12, 236 ], - "strength": 0.35318368673324585, + "strength": 35, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6260275840759277 + "terrain_modification_cost": 163 }, { "attractor_type": "PassEntrance", @@ -1521,9 +1521,9 @@ 13, 40 ], - "strength": 0.005219221115112305, + "strength": 1, "sub_biome": "Alpine", - "terrain_modification_cost": 4.065165996551514 + "terrain_modification_cost": 407 }, { "attractor_type": "PassEntrance", @@ -1531,9 +1531,9 @@ 14, 28 ], - "strength": 0.057306885719299316, + "strength": 6, "sub_biome": "Alpine", - "terrain_modification_cost": 3.8907318115234375 + "terrain_modification_cost": 389 }, { "attractor_type": "PassEntrance", @@ -1541,9 +1541,9 @@ 17, 6 ], - "strength": 0.16951984167099, + "strength": 17, "sub_biome": "Alpine", - "terrain_modification_cost": 3.8207366466522217 + "terrain_modification_cost": 382 }, { "attractor_type": "PassEntrance", @@ -1551,9 +1551,9 @@ 17, 97 ], - "strength": 0.4840814471244812, + "strength": 48, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.5374383926391602 + "terrain_modification_cost": 154 }, { "attractor_type": "PassEntrance", @@ -1561,9 +1561,9 @@ 17, 127 ], - "strength": 0.4898225665092468, + "strength": 49, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.5755822658538818 + "terrain_modification_cost": 158 }, { "attractor_type": "PassEntrance", @@ -1571,9 +1571,9 @@ 18, 168 ], - "strength": 0.42275571823120117, + "strength": 42, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.5530509948730469 + "terrain_modification_cost": 155 }, { "attractor_type": "PassEntrance", @@ -1581,9 +1581,9 @@ 19, 180 ], - "strength": 0.4895615577697754, + "strength": 49, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.5998942852020264 + "terrain_modification_cost": 160 }, { "attractor_type": "PassEntrance", @@ -1591,9 +1591,9 @@ 19, 224 ], - "strength": 0.1772964596748352, + "strength": 18, "sub_biome": "Alpine", - "terrain_modification_cost": 3.8650248050689697 + "terrain_modification_cost": 387 }, { "attractor_type": "PassEntrance", @@ -1601,9 +1601,9 @@ 25, 40 ], - "strength": 0.28909188508987427, + "strength": 29, "sub_biome": "BorealForest", - "terrain_modification_cost": 2.1497459411621094 + "terrain_modification_cost": 215 }, { "attractor_type": "PassEntrance", @@ -1611,9 +1611,9 @@ 27, 81 ], - "strength": 0.337421715259552, + "strength": 34, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.3350704908370972 + "terrain_modification_cost": 134 }, { "attractor_type": "PassEntrance", @@ -1621,9 +1621,9 @@ 27, 155 ], - "strength": 0.26831942796707153, + "strength": 27, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.7128292322158813 + "terrain_modification_cost": 171 }, { "attractor_type": "PassEntrance", @@ -1631,9 +1631,9 @@ 27, 236 ], - "strength": 0.13883090019226074, + "strength": 14, "sub_biome": "Alpine", - "terrain_modification_cost": 3.861788034439087 + "terrain_modification_cost": 386 }, { "attractor_type": "PassEntrance", @@ -1641,9 +1641,9 @@ 29, 15 ], - "strength": 0.48554277420043945, + "strength": 49, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.643457293510437 + "terrain_modification_cost": 164 }, { "attractor_type": "PassEntrance", @@ -1651,9 +1651,9 @@ 29, 61 ], - "strength": 0.39337158203125, + "strength": 39, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.679581880569458 + "terrain_modification_cost": 168 }, { "attractor_type": "PassEntrance", @@ -1661,9 +1661,9 @@ 32, 251 ], - "strength": 0.27077245712280273, + "strength": 27, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.554060935974121 + "terrain_modification_cost": 155 }, { "attractor_type": "PassEntrance", @@ -1671,9 +1671,9 @@ 33, 0 ], - "strength": 0.165657639503479, + "strength": 17, "sub_biome": "Alpine", - "terrain_modification_cost": 4.308073043823242 + "terrain_modification_cost": 431 }, { "attractor_type": "PassEntrance", @@ -1681,9 +1681,9 @@ 36, 143 ], - "strength": 0.4602818489074707, + "strength": 46, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.4441801309585571 + "terrain_modification_cost": 144 }, { "attractor_type": "PassEntrance", @@ -1691,9 +1691,9 @@ 41, 235 ], - "strength": 0.36127346754074097, + "strength": 36, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.3532330989837646 + "terrain_modification_cost": 135 }, { "attractor_type": "PassEntrance", @@ -1701,9 +1701,9 @@ 44, 100 ], - "strength": 0.42854905128479004, + "strength": 43, "sub_biome": "TemperateForest", - "terrain_modification_cost": 2.222402572631836 + "terrain_modification_cost": 222 }, { "attractor_type": "PassEntrance", @@ -1711,9 +1711,9 @@ 54, 216 ], - "strength": 0.48147183656692505, + "strength": 48, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.966017961502075 + "terrain_modification_cost": 297 }, { "attractor_type": "PassEntrance", @@ -1721,9 +1721,9 @@ 60, 120 ], - "strength": 0.4297494888305664, + "strength": 43, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.3012661933898926 + "terrain_modification_cost": 230 }, { "attractor_type": "PassEntrance", @@ -1731,9 +1731,9 @@ 63, 243 ], - "strength": 0.4705114960670471, + "strength": 47, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.150148630142212 + "terrain_modification_cost": 215 }, { "attractor_type": "PassEntrance", @@ -1741,9 +1741,9 @@ 68, 63 ], - "strength": 0.23001044988632202, + "strength": 23, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.0354530811309814 + "terrain_modification_cost": 204 }, { "attractor_type": "PassEntrance", @@ -1751,9 +1751,9 @@ 69, 205 ], - "strength": 0.24180585145950317, + "strength": 24, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.095156192779541 + "terrain_modification_cost": 210 }, { "attractor_type": "PassEntrance", @@ -1761,9 +1761,9 @@ 74, 231 ], - "strength": 0.33053237199783325, + "strength": 33, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.239078998565674 + "terrain_modification_cost": 224 }, { "attractor_type": "PassEntrance", @@ -1771,9 +1771,9 @@ 75, 78 ], - "strength": 0.2968685030937195, + "strength": 30, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.100158214569092 + "terrain_modification_cost": 210 }, { "attractor_type": "PassEntrance", @@ -1781,9 +1781,9 @@ 114, 41 ], - "strength": 0.4360647201538086, + "strength": 44, "sub_biome": "BorealForest", - "terrain_modification_cost": 2.107067108154297 + "terrain_modification_cost": 211 }, { "attractor_type": "LakeShore", @@ -1791,9 +1791,9 @@ 26, 116 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.367119073867798 + "terrain_modification_cost": 337 }, { "attractor_type": "LakeShore", @@ -1801,9 +1801,9 @@ 26, 128 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.4626827239990234 + "terrain_modification_cost": 346 }, { "attractor_type": "LakeShore", @@ -1811,9 +1811,9 @@ 31, 16 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 4.126629829406738 + "terrain_modification_cost": 413 }, { "attractor_type": "LakeShore", @@ -1821,9 +1821,9 @@ 33, 100 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3024113178253174 + "terrain_modification_cost": 330 }, { "attractor_type": "LakeShore", @@ -1831,9 +1831,9 @@ 36, 49 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.346609354019165 + "terrain_modification_cost": 335 }, { "attractor_type": "LakeShore", @@ -1841,9 +1841,9 @@ 36, 147 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.4708170890808105 + "terrain_modification_cost": 347 }, { "attractor_type": "LakeShore", @@ -1851,9 +1851,9 @@ 38, 61 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2546582221984863 + "terrain_modification_cost": 325 }, { "attractor_type": "LakeShore", @@ -1861,9 +1861,9 @@ 38, 127 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.28804612159729 + "terrain_modification_cost": 329 }, { "attractor_type": "LakeShore", @@ -1871,9 +1871,9 @@ 41, 112 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.2563929557800293 + "terrain_modification_cost": 326 }, { "attractor_type": "LakeShore", @@ -1881,9 +1881,9 @@ 57, 52 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.285806655883789 + "terrain_modification_cost": 329 }, { "attractor_type": "LakeShore", @@ -1891,9 +1891,9 @@ 68, 15 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.294482469558716 + "terrain_modification_cost": 329 }, { "attractor_type": "LakeShore", @@ -1901,9 +1901,9 @@ 95, 220 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.4050469398498535 + "terrain_modification_cost": 341 }, { "attractor_type": "LakeShore", @@ -1911,9 +1911,9 @@ 116, 50 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.433655261993408 + "terrain_modification_cost": 343 }, { "attractor_type": "LakeShore", @@ -1921,9 +1921,9 @@ 117, 75 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.215609550476074 + "terrain_modification_cost": 322 }, { "attractor_type": "LakeShore", @@ -1931,9 +1931,9 @@ 117, 90 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.27481746673584 + "terrain_modification_cost": 327 }, { "attractor_type": "LakeShore", @@ -1941,9 +1941,9 @@ 125, 102 ], - "strength": 0.5, + "strength": 50, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3229317665100098 + "terrain_modification_cost": 332 }, { "attractor_type": "PlainCenter", @@ -1951,9 +1951,9 @@ 0, 99 ], - "strength": 0.34904980659484863, + "strength": 35, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6170393228530884 + "terrain_modification_cost": 162 }, { "attractor_type": "PlainCenter", @@ -1961,9 +1961,9 @@ 0, 143 ], - "strength": 0.35175231099128723, + "strength": 35, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6659718751907349 + "terrain_modification_cost": 167 }, { "attractor_type": "PlainCenter", @@ -1971,9 +1971,19 @@ 0, 196 ], - "strength": 0.3439139425754547, + "strength": 34, "sub_biome": "Tundra", - "terrain_modification_cost": 1.65461266040802 + "terrain_modification_cost": 165 + }, + { + "attractor_type": "PlainCenter", + "position": [ + 4, + 131 + ], + "strength": 33, + "sub_biome": "Tundra", + "terrain_modification_cost": 162 }, { "attractor_type": "PlainCenter", @@ -1981,9 +1991,9 @@ 16, 142 ], - "strength": 0.3768337666988373, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4972498416900635 + "terrain_modification_cost": 150 }, { "attractor_type": "PlainCenter", @@ -1991,9 +2001,9 @@ 18, 182 ], - "strength": 0.3360206186771393, + "strength": 34, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.548561692237854 + "terrain_modification_cost": 155 }, { "attractor_type": "PlainCenter", @@ -2001,9 +2011,9 @@ 21, 85 ], - "strength": 0.35864385962486267, + "strength": 36, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.5673878192901611 + "terrain_modification_cost": 157 }, { "attractor_type": "PlainCenter", @@ -2011,9 +2021,9 @@ 24, 100 ], - "strength": 0.3896811902523041, + "strength": 39, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.455227255821228 + "terrain_modification_cost": 146 }, { "attractor_type": "PlainCenter", @@ -2021,9 +2031,9 @@ 25, 120 ], - "strength": 0.37719008326530457, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5635943412780762 + "terrain_modification_cost": 156 }, { "attractor_type": "PlainCenter", @@ -2031,9 +2041,9 @@ 27, 17 ], - "strength": 0.35519692301750183, + "strength": 36, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.3312914371490479 + "terrain_modification_cost": 133 }, { "attractor_type": "PlainCenter", @@ -2041,9 +2051,19 @@ 29, 138 ], - "strength": 0.3820492923259735, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.451548457145691 + "terrain_modification_cost": 145 + }, + { + "attractor_type": "PlainCenter", + "position": [ + 31, + 37 + ], + "strength": 33, + "sub_biome": "TemperateForest", + "terrain_modification_cost": 138 }, { "attractor_type": "PlainCenter", @@ -2051,9 +2071,9 @@ 31, 182 ], - "strength": 0.3792470097541809, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.512524127960205 + "terrain_modification_cost": 151 }, { "attractor_type": "PlainCenter", @@ -2061,9 +2081,9 @@ 32, 155 ], - "strength": 0.35981717705726624, + "strength": 36, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.560078501701355 + "terrain_modification_cost": 156 }, { "attractor_type": "PlainCenter", @@ -2071,9 +2091,9 @@ 35, 60 ], - "strength": 0.34876736998558044, + "strength": 35, "sub_biome": "Wetland", - "terrain_modification_cost": 3.318408489227295 + "terrain_modification_cost": 332 }, { "attractor_type": "PlainCenter", @@ -2081,9 +2101,9 @@ 35, 87 ], - "strength": 0.36647558212280273, + "strength": 37, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.3807445764541626 + "terrain_modification_cost": 138 }, { "attractor_type": "PlainCenter", @@ -2091,9 +2111,9 @@ 35, 167 ], - "strength": 0.37952160835266113, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4533195495605469 + "terrain_modification_cost": 145 }, { "attractor_type": "PlainCenter", @@ -2101,9 +2121,9 @@ 35, 197 ], - "strength": 0.3724784851074219, + "strength": 37, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5570673942565918 + "terrain_modification_cost": 156 }, { "attractor_type": "PlainCenter", @@ -2111,9 +2131,9 @@ 39, 212 ], - "strength": 0.37682634592056274, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5015374422073364 + "terrain_modification_cost": 150 }, { "attractor_type": "PlainCenter", @@ -2121,9 +2141,9 @@ 41, 2 ], - "strength": 0.3762492835521698, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5276914834976196 + "terrain_modification_cost": 153 }, { "attractor_type": "PlainCenter", @@ -2131,9 +2151,9 @@ 41, 17 ], - "strength": 0.3774108588695526, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4569072723388672 + "terrain_modification_cost": 146 }, { "attractor_type": "PlainCenter", @@ -2141,9 +2161,9 @@ 43, 30 ], - "strength": 0.3719059228897095, + "strength": 37, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.3577040433883667 + "terrain_modification_cost": 136 }, { "attractor_type": "PlainCenter", @@ -2151,9 +2171,9 @@ 43, 42 ], - "strength": 0.3821267783641815, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4176174402236938 + "terrain_modification_cost": 142 }, { "attractor_type": "PlainCenter", @@ -2161,9 +2181,9 @@ 44, 253 ], - "strength": 0.3656153380870819, + "strength": 37, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.3666911125183105 + "terrain_modification_cost": 137 }, { "attractor_type": "PlainCenter", @@ -2171,9 +2191,9 @@ 45, 229 ], - "strength": 0.3548292815685272, + "strength": 35, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.548870325088501 + "terrain_modification_cost": 155 }, { "attractor_type": "PlainCenter", @@ -2181,9 +2201,9 @@ 46, 72 ], - "strength": 0.3783442974090576, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.490007996559143 + "terrain_modification_cost": 149 }, { "attractor_type": "PlainCenter", @@ -2191,9 +2211,9 @@ 47, 109 ], - "strength": 0.3770926892757416, + "strength": 38, "sub_biome": "TemperateForest", - "terrain_modification_cost": 1.3448281288146973 + "terrain_modification_cost": 134 }, { "attractor_type": "PlainCenter", @@ -2201,9 +2221,9 @@ 47, 155 ], - "strength": 0.38002920150756836, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4749995470046997 + "terrain_modification_cost": 147 }, { "attractor_type": "PlainCenter", @@ -2211,9 +2231,9 @@ 50, 123 ], - "strength": 0.3780028522014618, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.450942873954773 + "terrain_modification_cost": 145 }, { "attractor_type": "PlainCenter", @@ -2221,9 +2241,9 @@ 51, 54 ], - "strength": 0.38144412636756897, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4597254991531372 + "terrain_modification_cost": 146 }, { "attractor_type": "PlainCenter", @@ -2231,19 +2251,9 @@ 51, 138 ], - "strength": 0.373098224401474, + "strength": 37, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5520182847976685 - }, - { - "attractor_type": "PlainCenter", - "position": [ - 52, - 192 - ], - "strength": 0.3336641788482666, - "sub_biome": "Wetland", - "terrain_modification_cost": 3.2407350540161133 + "terrain_modification_cost": 155 }, { "attractor_type": "PlainCenter", @@ -2251,9 +2261,9 @@ 56, 250 ], - "strength": 0.3799034655094147, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5098344087600708 + "terrain_modification_cost": 151 }, { "attractor_type": "PlainCenter", @@ -2261,9 +2271,9 @@ 57, 228 ], - "strength": 0.37485271692276, + "strength": 37, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.0317604541778564 + "terrain_modification_cost": 203 }, { "attractor_type": "PlainCenter", @@ -2271,9 +2281,9 @@ 60, 66 ], - "strength": 0.34856030344963074, + "strength": 35, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3617842197418213 + "terrain_modification_cost": 336 }, { "attractor_type": "PlainCenter", @@ -2281,9 +2291,9 @@ 61, 31 ], - "strength": 0.35544154047966003, + "strength": 36, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4494177103042603 + "terrain_modification_cost": 145 }, { "attractor_type": "PlainCenter", @@ -2291,9 +2301,9 @@ 65, 183 ], - "strength": 0.36474114656448364, + "strength": 36, "sub_biome": "TropicalWet", - "terrain_modification_cost": 2.123445987701416 + "terrain_modification_cost": 212 }, { "attractor_type": "PlainCenter", @@ -2301,9 +2311,9 @@ 66, 3 ], - "strength": 0.34397411346435547, + "strength": 34, "sub_biome": "Wetland", - "terrain_modification_cost": 3.283626079559326 + "terrain_modification_cost": 328 }, { "attractor_type": "PlainCenter", @@ -2311,9 +2321,9 @@ 66, 43 ], - "strength": 0.35694727301597595, + "strength": 36, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4528050422668457 + "terrain_modification_cost": 145 }, { "attractor_type": "PlainCenter", @@ -2321,9 +2331,9 @@ 67, 148 ], - "strength": 0.3692795932292938, + "strength": 37, "sub_biome": "Wetland", - "terrain_modification_cost": 3.251011371612549 + "terrain_modification_cost": 325 }, { "attractor_type": "PlainCenter", @@ -2331,9 +2341,9 @@ 72, 55 ], - "strength": 0.3631070554256439, + "strength": 36, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.5532464981079102 + "terrain_modification_cost": 155 }, { "attractor_type": "PlainCenter", @@ -2341,9 +2351,9 @@ 75, 22 ], - "strength": 0.3673618733882904, + "strength": 37, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4995574951171875 + "terrain_modification_cost": 150 }, { "attractor_type": "PlainCenter", @@ -2351,9 +2361,9 @@ 76, 69 ], - "strength": 0.37490126490592957, + "strength": 37, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4920836687088013 + "terrain_modification_cost": 149 }, { "attractor_type": "PlainCenter", @@ -2361,9 +2371,9 @@ 85, 43 ], - "strength": 0.3636673092842102, + "strength": 36, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4653754234313965 + "terrain_modification_cost": 147 }, { "attractor_type": "PlainCenter", @@ -2371,19 +2381,9 @@ 86, 216 ], - "strength": 0.38846680521965027, + "strength": 39, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4370285272598267 - }, - { - "attractor_type": "PlainCenter", - "position": [ - 87, - 147 - ], - "strength": 0.3334938585758209, - "sub_biome": "Wetland", - "terrain_modification_cost": 3.30601167678833 + "terrain_modification_cost": 144 }, { "attractor_type": "PlainCenter", @@ -2391,9 +2391,9 @@ 87, 204 ], - "strength": 0.370309442281723, + "strength": 37, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4930760860443115 + "terrain_modification_cost": 149 }, { "attractor_type": "PlainCenter", @@ -2401,9 +2401,9 @@ 107, 219 ], - "strength": 0.35765179991722107, + "strength": 36, "sub_biome": "BorealForest", - "terrain_modification_cost": 1.6396077871322632 + "terrain_modification_cost": 164 }, { "attractor_type": "PlainCenter", @@ -2411,9 +2411,9 @@ 108, 207 ], - "strength": 0.3772318959236145, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.475163459777832 + "terrain_modification_cost": 148 }, { "attractor_type": "PlainCenter", @@ -2421,9 +2421,9 @@ 112, 231 ], - "strength": 0.38103756308555603, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.499894380569458 + "terrain_modification_cost": 150 }, { "attractor_type": "PlainCenter", @@ -2431,9 +2431,9 @@ 114, 13 ], - "strength": 0.380348265171051, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4645832777023315 + "terrain_modification_cost": 146 }, { "attractor_type": "PlainCenter", @@ -2441,9 +2441,9 @@ 115, 64 ], - "strength": 0.37335869669914246, + "strength": 37, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4712164402008057 + "terrain_modification_cost": 147 }, { "attractor_type": "PlainCenter", @@ -2451,9 +2451,9 @@ 115, 85 ], - "strength": 0.3454594314098358, + "strength": 35, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.426665186882019 + "terrain_modification_cost": 143 }, { "attractor_type": "PlainCenter", @@ -2461,9 +2461,9 @@ 116, 100 ], - "strength": 0.37485629320144653, + "strength": 37, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4485889673233032 + "terrain_modification_cost": 145 }, { "attractor_type": "PlainCenter", @@ -2471,9 +2471,9 @@ 120, 25 ], - "strength": 0.3743213713169098, + "strength": 37, "sub_biome": "Tundra", - "terrain_modification_cost": 1.669413685798645 + "terrain_modification_cost": 167 }, { "attractor_type": "PlainCenter", @@ -2481,9 +2481,9 @@ 120, 201 ], - "strength": 0.3818639814853668, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.491018533706665 + "terrain_modification_cost": 149 }, { "attractor_type": "PlainCenter", @@ -2491,9 +2491,9 @@ 123, 46 ], - "strength": 0.34651970863342285, + "strength": 35, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4500824213027954 + "terrain_modification_cost": 145 }, { "attractor_type": "PlainCenter", @@ -2501,9 +2501,9 @@ 124, 120 ], - "strength": 0.383791446685791, + "strength": 38, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4295201301574707 + "terrain_modification_cost": 143 }, { "attractor_type": "PlainCenter", @@ -2511,9 +2511,9 @@ 124, 132 ], - "strength": 0.35981518030166626, + "strength": 36, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4080733060836792 + "terrain_modification_cost": 141 }, { "attractor_type": "PlainCenter", @@ -2521,9 +2521,9 @@ 127, 65 ], - "strength": 0.35404202342033386, + "strength": 35, "sub_biome": "CoastalLowland", - "terrain_modification_cost": 1.4079231023788452 + "terrain_modification_cost": 141 }, { "attractor_type": "PlainCenter", @@ -2531,9 +2531,9 @@ 127, 144 ], - "strength": 0.3410905599594116, + "strength": 34, "sub_biome": "Wetland", - "terrain_modification_cost": 3.35347843170166 + "terrain_modification_cost": 335 }, { "attractor_type": "PlainCenter", @@ -2541,9 +2541,9 @@ 127, 213 ], - "strength": 0.33604899048805237, + "strength": 34, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6432392597198486 + "terrain_modification_cost": 164 }, { "attractor_type": "PlainCenter", @@ -2551,9 +2551,9 @@ 127, 231 ], - "strength": 0.36933061480522156, + "strength": 37, "sub_biome": "Tundra", - "terrain_modification_cost": 1.6374884843826294 + "terrain_modification_cost": 164 }, { "attractor_type": "PlainCenter", @@ -2561,9 +2561,9 @@ 127, 251 ], - "strength": 0.34192124009132385, + "strength": 34, "sub_biome": "Wetland", - "terrain_modification_cost": 3.3208022117614746 + "terrain_modification_cost": 332 } ], "body_id": "GJ1c",