Repository: Leafwing-Studios/Emergence (rust)

Issue:
Change water properties based on soil type
We want to vary:

- water holding capacity
- evaporation rate
- lateral diffusion rate

These should all be properties on `TerrainData` (relative to a platonic soil with a value of 1.0 in each of these), and loaded from disk via a manifest.

Pull request description:
Vary soil water properties by soil type
Fixes #785.

Patch:
diff --git a/emergence_game/assets/manifests/base_game.terrain_manifest.json b/emergence_game/assets/manifests/base_game.terrain_manifest.json
index a9c2bcb..62245c3 100644
--- a/emergence_game/assets/manifests/base_game.terrain_manifest.json
+++ b/emergence_game/assets/manifests/base_game.terrain_manifest.json
@@ -2,13 +2,22 @@
   "$schema": "./schema/terrain.schema.json",
   "terrain_types": {
     "swampy": {
-      "walking_speed": 0.5
+      "walking_speed": 0.5,
+      "water_capacity": 0.7,
+      "water_flow_rate": 0.4,
+      "water_evaporation_rate": 0.6
     },
     "rocky": {
-      "walking_speed": 2.0
+      "walking_speed": 1.5,
+      "water_capacity": 0.1,
+      "water_flow_rate": 0.05,
+      "water_evaporation_rate": 0.3
     },
     "grassy": {
-      "walking_speed": 1.0
+      "walking_speed": 1.0,
+      "water_capacity": 0.3,
+      "water_flow_rate": 0.3,
+      "water_evaporation_rate": 0.1
     }
   }
 }
\ No newline at end of file
diff --git a/emergence_lib/src/terrain/terrain_manifest.rs b/emergence_lib/src/terrain/terrain_manifest.rs
index 46d1e4c..6fa6d1f 100644
--- a/emergence_lib/src/terrain/terrain_manifest.rs
+++ b/emergence_lib/src/terrain/terrain_manifest.rs
@@ -25,6 +25,21 @@ pub struct TerrainData {
     ///
     /// Note that this only affects the walking speed of units that are not on a path.
     pub walking_speed: f32,
+    /// The amount of water that can be stored in one volume of this terrain type.
+    ///
+    /// This is relative to empty space, which has a capacity of 1.0.
+    /// Generally this value should be between 0.05 and 0.5.
+    pub water_capacity: f32,
+    /// The relative rate at which water flows through this terrain type.
+    ///
+    /// This is relative to empty space, which has a flow rate of 1.0.
+    /// Generally this value should be between 0.05 and 0.3.
+    pub water_flow_rate: f32,
+    /// The evaporation rate of water from this terrain type.
+    ///
+    /// This is relative to empty space, which has an evaporation rate of 1.0.
+    /// Generally this value should be between 0.05 and 0.5.
+    pub water_evaporation_rate: f32,
 }
 
 /// The [`TerrainManifest`] as seen in the manifest file.
diff --git a/emergence_lib/src/water/mod.rs b/emergence_lib/src/water/mod.rs
index f9561a9..e15b459 100644
--- a/emergence_lib/src/water/mod.rs
+++ b/emergence_lib/src/water/mod.rs
@@ -10,6 +10,7 @@ use bevy::{prelude::*, utils::HashMap};
 use derive_more::{Add, AddAssign, Sub, SubAssign};
 
 use crate::simulation::time::Days;
+use crate::terrain::terrain_manifest::{Terrain, TerrainManifest};
 use crate::{
     asset_management::manifest::Id,
     items::item_manifest::{Item, ItemManifest},
@@ -33,12 +34,12 @@ pub mod roots;
 mod water_dynamics;
 
 /// Controls the key parameters of water movement and behavior.
+///
+/// Note that soil properties are stored seperately for each soil type in [`TerrainData`](crate::terrain::terrain_manifest::TerrainData).
 #[derive(Resource, Debug, Clone, Copy)]
 pub(crate) struct WaterConfig {
     /// The rate of evaporation per day from each tile.
     evaporation_rate: Height,
-    /// The relative rate of evaporation from soil.
-    soil_evaporation_ratio: f32,
     /// The rate of precipitation per day on each tile.
     precipitation_rate: Height,
     /// The amount of water that is deposited per day on the tile of each water emitter.
@@ -47,10 +48,6 @@ pub(crate) struct WaterConfig {
     emission_pressure: Height,
     /// The number of water items produced for each full tile of water.
     water_items_per_tile: f32,
-    /// The amount of water stored in a tile of soil relative to a pure tile of water.
-    ///
-    /// This value should be less than 1 and must be greater than 0.
-    relative_soil_water_capacity: f32,
     /// The rate at which water moves horizontally.
     ///
     /// The units are cubic tiles per day per tile of height difference.
@@ -59,8 +56,6 @@ pub(crate) struct WaterConfig {
     ///
     /// If this value becomes too large, the simulation may become unstable, with water alternating between fully flooded and fully dry tiles.
     lateral_flow_rate: f32,
-    /// The relative rate at which water moves horizontally through soil.
-    soil_lateral_flow_ratio: f32,
     /// Are oceans enabled?
     pub(crate) enable_oceans: bool,
     /// Controls the behavior of the tides.
@@ -71,14 +66,11 @@ impl WaterConfig {
     /// The default configuration for in-game water behavior.
     const IN_GAME: Self = Self {
         evaporation_rate: Height(2.0),
-        soil_evaporation_ratio: 0.2,
         precipitation_rate: Height(2.0),
         emission_rate: Volume(1e4),
         emission_pressure: Height(1.0),
         water_items_per_tile: 50.0,
-        relative_soil_water_capacity: 0.2,
         lateral_flow_rate: 1e3,
-        soil_lateral_flow_ratio: 0.2,
         enable_oceans: true,
         tide_settings: TideSettings {
             amplitude: Height(3.0),
@@ -91,14 +83,11 @@ impl WaterConfig {
     #[allow(dead_code)]
     const NULL: Self = Self {
         evaporation_rate: Height(0.0),
-        soil_evaporation_ratio: 0.0,
         precipitation_rate: Height(0.0),
         emission_rate: Volume(0.0),
         emission_pressure: Height(0.0),
         water_items_per_tile: 0.0,
-        relative_soil_water_capacity: 0.5,
         lateral_flow_rate: 0.0,
-        soil_lateral_flow_ratio: 0.0,
         enable_oceans: false,
         tide_settings: TideSettings {
             amplitude: Height(0.0),
@@ -173,7 +162,8 @@ impl Plugin for WaterPlugin {
                     // It is important that the computed height of the water is accurate before we start moving it around.
                     update_water_depth
                         .after(WaterSet::VerticalWaterMovement)
-                        .before(WaterSet::HorizontalWaterMovement),
+                        .before(WaterSet::HorizontalWaterMovement)
+                        .in_set(SimulationSet),
                 )
                 .add_system(horizontal_water_movement.in_set(WaterSet::HorizontalWaterMovement))
                 .add_systems(
@@ -380,8 +370,9 @@ impl Display for WaterDepth {
 /// Updates the depth of water at each tile based on the volume of water and soil properties.
 fn update_water_depth(
     mut water_table: ResMut<WaterTable>,
-    water_config: Res<WaterConfig>,
     map_geometry: Res<MapGeometry>,
+    query: Query<&Id<Terrain>>,
+    terrain_manifest: Res<TerrainManifest>,
 ) {
     water_table.water_depth.clear();
 
@@ -391,13 +382,13 @@ fn update_water_depth(
     for tile_pos in map_geometry.valid_tile_positions() {
         let soil_height = map_geometry.get_height(tile_pos).unwrap_or_default();
         let water_volume = water_table.get_volume(tile_pos);
+        let terrain_entity = map_geometry.get_terrain(tile_pos).unwrap();
+        let terrain_id = *query.get(terrain_entity).unwrap();
+
+        let relative_soil_water_capacity = terrain_manifest.get(terrain_id).water_capacity;
 
-        let water_depth = WaterDepth::compute(
-            water_volume,
-            soil_height,
-            // TODO: vary this based on soil type
-            water_config.relative_soil_water_capacity,
-        );
+        let water_depth =
+            WaterDepth::compute(water_volume, soil_height, relative_soil_water_capacity);
 
         water_table.water_depth.insert(tile_pos, water_depth);
     }
diff --git a/emergence_lib/src/water/water_dynamics.rs b/emergence_lib/src/water/water_dynamics.rs
index 8dd4eef..8cb20a0 100644
--- a/emergence_lib/src/water/water_dynamics.rs
+++ b/emergence_lib/src/water/water_dynamics.rs
@@ -2,10 +2,14 @@
 
 use bevy::{prelude::*, utils::HashMap};
 
-use crate::simulation::{
-    geometry::{Height, MapGeometry, TilePos, Volume},
-    time::InGameTime,
-    weather::CurrentWeather,
+use crate::{
+    asset_management::manifest::Id,
+    simulation::{
+        geometry::{Height, MapGeometry, TilePos, Volume},
+        time::InGameTime,
+        weather::CurrentWeather,
+    },
+    terrain::terrain_manifest::{Terrain, TerrainManifest},
 };
 
 use super::{FlowVelocity, WaterConfig, WaterTable};
@@ -13,6 +17,8 @@ use super::{FlowVelocity, WaterConfig, WaterTable};
 /// Evaporates water from surface water.
 pub(super) fn evaporation(
     mut water_table: ResMut<WaterTable>,
+    terrain_query: Query<&Id<Terrain>>,
+    terrain_manifest: Res<TerrainManifest>,
     water_config: Res<WaterConfig>,
     map_geometry: Res<MapGeometry>,
     in_game_time: Res<InGameTime>,
@@ -30,7 +36,11 @@ pub(super) fn evaporation(
         let total_evaporated = if water_table.surface_water_depth(tile_pos) > Height::ZERO {
             Volume(evaporation_rate)
         } else {
-            Volume(evaporation_rate * water_config.soil_evaporation_ratio)
+            let terrain_entity = map_geometry.get_terrain(tile_pos).unwrap();
+            let terrain_id = *terrain_query.get(terrain_entity).unwrap();
+            let evaporation_ratio = terrain_manifest.get(terrain_id).water_evaporation_rate;
+
+            Volume(evaporation_rate * evaporation_ratio)
         };
 
         water_table.remove(tile_pos, total_evaporated);
@@ -62,6 +72,8 @@ pub(super) fn precipitation(
 /// Moves water from one tile to another, according to the relative height of the water table.
 pub(super) fn horizontal_water_movement(
     mut water_table: ResMut<WaterTable>,
+    terrain_query: Query<&Id<Terrain>>,
+    terrain_manifest: Res<TerrainManifest>,
     water_config: Res<WaterConfig>,
     map_geometry: Res<MapGeometry>,
     fixed_time: Res<FixedTime>,
@@ -96,6 +108,8 @@ pub(super) fn horizontal_water_movement(
             &water_config,
             &map_geometry,
             &water_table,
+            &terrain_query,
+            &terrain_manifest,
         );
 
         // Ensure that we divide the water evenly between all neighbors
@@ -137,10 +151,15 @@ pub(super) fn horizontal_water_movement(
                 let neighbor_tile_height =
                     map_geometry.get_height(valid_neighbor).unwrap_or_default();
                 let neighbor_water_height = water_table.get_height(valid_neighbor, &map_geometry);
+                let neighbor_terrain_entity = map_geometry.get_terrain(valid_neighbor).unwrap();
+                let neighbor_terrain_id = *terrain_query.get(neighbor_terrain_entity).unwrap();
+                let neighbor_soil_lateral_flow_ratio =
+                    terrain_manifest.get(neighbor_terrain_id).water_flow_rate;
 
                 let proposed_water_transfer = lateral_flow(
                     base_water_transfer_amount,
-                    &water_config,
+                    1.0,
+                    neighbor_soil_lateral_flow_ratio,
                     Height::ZERO,
                     neighbor_tile_height,
                     water_table.ocean_height,
@@ -178,6 +197,8 @@ fn proposed_lateral_flow_to_neighbors(
     water_config: &WaterConfig,
     map_geometry: &MapGeometry,
     water_table: &WaterTable,
+    terrain_query: &Query<&Id<Terrain>>,
+    terrain_manifest: &TerrainManifest,
 ) -> HashMap<TilePos, Volume> {
     let water_height = water_table.get_height(tile_pos, map_geometry);
 
@@ -185,6 +206,9 @@ fn proposed_lateral_flow_to_neighbors(
     // This is important because we need to be able to transfer water off the edge of the map.
     let neighbors = tile_pos.all_neighbors();
     let mut water_to_neighbors = HashMap::default();
+    let terrain_entity = map_geometry.get_terrain(tile_pos).unwrap();
+    let terrain_id = *terrain_query.get(terrain_entity).unwrap();
+    let soil_lateral_flow_ratio = terrain_manifest.get(terrain_id).water_flow_rate;
 
     for neighbor in neighbors {
         // Non-valid neighbors are treated as if they are ocean tiles, and cause water to flow off the edge of the map.
@@ -192,11 +216,22 @@ fn proposed_lateral_flow_to_neighbors(
             continue;
         }
 
+        let neighbor_soil_lateral_flow_ratio =
+            if let Some(neigbor_entity) = map_geometry.get_terrain(neighbor) {
+                let neighbor_id = *terrain_query.get(neigbor_entity).unwrap();
+                terrain_manifest.get(neighbor_id).water_flow_rate
+            } else {
+                // If the neighbor is not a valid tile, then it is an ocean tile.
+                // This should never have soil, but if it does, let it flow like surface water.
+                1.0
+            };
+
         let neighbor_water_height = water_table.get_height(neighbor, map_geometry);
 
         let proposed_water_transfer = lateral_flow(
             base_water_transfer_amount,
-            water_config,
+            soil_lateral_flow_ratio,
+            neighbor_soil_lateral_flow_ratio,
             map_geometry.get_height(tile_pos).unwrap_or_default(),
             map_geometry.get_height(neighbor).unwrap_or_default(),
             water_height,
@@ -214,7 +249,8 @@ fn proposed_lateral_flow_to_neighbors(
 #[must_use]
 fn lateral_flow(
     base_water_transfer_amount: f32,
-    water_config: &WaterConfig,
+    soil_lateral_flow_ratio: f32,
+    neighbor_soil_lateral_flow_ratio: f32,
     tile_height: Height,
     neighbor_tile_height: Height,
     water_height: Height,
@@ -225,8 +261,6 @@ fn lateral_flow(
     assert!(neighbor_water_height >= Height::ZERO);
     assert!(tile_height >= Height::ZERO);
     assert!(neighbor_tile_height >= Height::ZERO);
-    assert!(water_config.soil_lateral_flow_ratio >= 0.);
-    assert!(water_config.soil_lateral_flow_ratio <= 1.);
 
     // If the water is higher than the neighbor, move water from the tile to the neighbor
     // at a rate proportional to the height difference.
@@ -246,8 +280,9 @@ fn lateral_flow(
     // Water flows more easily between tiles that are both flooded.
     let medium_coefficient = match (surface_water_present, neighbor_surface_water_present) {
         (true, true) => 1.,
-        (false, false) => water_config.soil_lateral_flow_ratio,
-        _ => (1. + water_config.soil_lateral_flow_ratio) / 2.,
+        (false, false) => (soil_lateral_flow_ratio + neighbor_soil_lateral_flow_ratio) / 2.,
+        (true, false) => (1. + soil_lateral_flow_ratio) / 2.,
+        (false, true) => (1. + neighbor_soil_lateral_flow_ratio) / 2.,
     };
 
     let proposed_amount = Volume::from_height(
@@ -274,6 +309,7 @@ mod tests {
     use crate::simulation::time::advance_in_game_time;
     use crate::simulation::weather::{Weather, WeatherPlugin};
     use crate::simulation::SimulationSet;
+    use crate::terrain::terrain_manifest::TerrainData;
     use crate::water::WaterPlugin;
 
     use super::*;
@@ -307,7 +343,7 @@ mod tests {
                     .in_schedule(CoreSchedule::FixedUpdate),
             );
 
-        let map_geometry = scenario
+        let mut map_geometry = scenario
             .map_shape
             .set_heights(scenario.map_size.map_geometry());
         let water_table = scenario.water_table_strategy.water_table(&map_geometry);
@@ -321,11 +357,38 @@ mod tests {
         }
 
         app.insert_resource(water_table);
-        app.insert_resource(map_geometry);
         // Override the default water config with one appropriate for testing.
         app.insert_resource(scenario.water_config);
         app.insert_resource(CurrentWeather::new(scenario.weather));
 
+        // Set the terrain properties.
+        let mut terrain_manifest = TerrainManifest::default();
+        terrain_manifest.insert(
+            "test".to_string(),
+            TerrainData {
+                walking_speed: 1.0,
+                water_capacity: 0.5,
+                water_flow_rate: 0.3,
+                water_evaporation_rate: 0.4,
+            },
+        );
+
+        app.world.insert_resource(terrain_manifest);
+
+        // Spawn terrain
+        for tile_pos in map_geometry
+            .valid_tile_positions()
+            .collect::<Vec<TilePos>>()
+        {
+            let terrain_entity = app
+                .world
+                .spawn((Id::<Terrain>::from_name("test".to_string()), tile_pos))
+                .id();
+            map_geometry.add_terrain(tile_pos, terrain_entity)
+        }
+
+        app.insert_resource(map_geometry);
+
         // Spawn emitter
         app.world.spawn((Landmark, TilePos::ZERO));
 
@@ -488,7 +551,6 @@ mod tests {
                         water_table_strategy,
                         water_config: WaterConfig {
                             evaporation_rate: Height(1.0),
-                            soil_evaporation_ratio: 0.5,
                             ..WaterConfig::NULL
                         },
                         weather: Weather::Clear,
@@ -580,7 +642,6 @@ mod tests {
                         emission_rate: Volume(1.0),
                         emission_pressure: Height(5.0),
                         lateral_flow_rate: 1000.,
-                        soil_lateral_flow_ratio: 0.5,
                         ..WaterConfig::NULL
                     },
                     weather: Weather::Clear,
@@ -806,14 +867,12 @@ mod tests {
 
     #[test]
     fn lateral_flow_moves_water_from_high_to_low() {
-        let water_config = WaterConfig {
-            lateral_flow_rate: 1.0,
-            ..WaterConfig::NULL
-        };
+        let soil_lateral_flow_ratio = 0.7;
 
         let water_transferred = lateral_flow(
             1.0,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             Height(1.0),
             Height(1.0),
             Height(2.0),
@@ -829,14 +888,12 @@ mod tests {
 
     #[test]
     fn lateral_flow_does_not_move_water_from_low_to_high() {
-        let water_config = WaterConfig {
-            lateral_flow_rate: 1.0,
-            ..WaterConfig::NULL
-        };
+        let soil_lateral_flow_ratio = 0.3;
 
         let water_transferred = lateral_flow(
             1.0,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             Height(1.0),
             Height(1.0),
             Height(1.0),
@@ -848,14 +905,12 @@ mod tests {
 
     #[test]
     fn lateral_flow_does_not_move_water_at_equal_heights() {
-        let water_config = WaterConfig {
-            lateral_flow_rate: 1.0,
-            ..WaterConfig::NULL
-        };
+        let soil_lateral_flow_ratio = 0.4;
 
         let water_transferred = lateral_flow(
             1.0,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             Height(1.0),
             Height(1.0),
             Height(1.0),
@@ -867,18 +922,15 @@ mod tests {
 
     #[test]
     fn surface_water_flows_faster() {
-        let water_config: WaterConfig = WaterConfig {
-            lateral_flow_rate: 1.0,
-            soil_lateral_flow_ratio: 0.1,
-            ..WaterConfig::NULL
-        };
-
         let water_height = Height(2.0);
         let neighbor_water_height = Height(1.0);
+        // This must be less than 1.0 for the test to be valid
+        let soil_lateral_flow_ratio = 0.5;
 
         let surface_water_flow = lateral_flow(
             1.0,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             Height(0.0),
             Height(0.0),
             water_height,
@@ -887,7 +939,8 @@ mod tests {
 
         let subsurface_water_flow = lateral_flow(
             1.0,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             Height(2.0),
             Height(2.0),
             water_height,
@@ -896,7 +949,8 @@ mod tests {
 
         let surface_to_soil_flow = lateral_flow(
             1.0,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             Height(0.0),
             Height(2.0),
             water_height,
@@ -905,7 +959,8 @@ mod tests {
 
         let soil_to_surface_flow = lateral_flow(
             1.0,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             Height(2.0),
             Height(0.0),
             water_height,
@@ -931,14 +986,12 @@ mod tests {
 
     #[test]
     fn lateral_water_flows_faster_with_larger_height_difference() {
-        let water_config: WaterConfig = WaterConfig {
-            lateral_flow_rate: 1.0,
-            ..WaterConfig::NULL
-        };
+        let soil_lateral_flow_ratio = 0.1;
 
         let small_height_difference = lateral_flow(
             1.0,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             Height(1.0),
             Height(1.0),
             Height(2.0),
@@ -947,7 +1000,8 @@ mod tests {
 
         let large_height_difference = lateral_flow(
             1.0,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             Height(1.0),
             Height(1.0),
             Height(3.0),
@@ -964,12 +1018,8 @@ mod tests {
 
     #[test]
     fn lateral_flow_eventually_equalizes_height_differences() {
-        let water_config: WaterConfig = WaterConfig {
-            lateral_flow_rate: 1.0,
-            ..WaterConfig::NULL
-        };
-
         let base_water_transfer_amount = 0.1;
+        let soil_lateral_flow_ratio = 0.1;
 
         let mut water_height_a = Height(2.0);
         let mut water_height_b = Height(1.0);
@@ -982,7 +1032,8 @@ mod tests {
         for _ in 0..100 {
             let water_transferred_a_to_b = lateral_flow(
                 base_water_transfer_amount,
-                &water_config,
+                soil_lateral_flow_ratio,
+                soil_lateral_flow_ratio,
                 tile_height_a,
                 tile_height_b,
                 water_height_a,
@@ -991,7 +1042,8 @@ mod tests {
 
             let water_transferred_b_to_a = lateral_flow(
                 base_water_transfer_amount,
-                &water_config,
+                soil_lateral_flow_ratio,
+                soil_lateral_flow_ratio,
                 tile_height_b,
                 tile_height_a,
                 water_height_b,
@@ -1034,10 +1086,7 @@ mod tests {
 
     #[test]
     fn lateral_flow_should_not_result_in_higher_neighbor() {
-        let water_config: WaterConfig = WaterConfig {
-            lateral_flow_rate: 1e5,
-            ..WaterConfig::NULL
-        };
+        let soil_lateral_flow_ratio = 1.0;
 
         // This is a very high transfer rate, to ensure that the water transfer is maximized
         let base_water_transfer_amount = 1e10;
@@ -1050,7 +1099,8 @@ mod tests {
 
         let water_transferred_a_to_b = lateral_flow(
             base_water_transfer_amount,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             tile_height_a,
             tile_height_b,
             water_height_a,
@@ -1059,7 +1109,8 @@ mod tests {
 
         let water_transferred_b_to_a = lateral_flow(
             base_water_transfer_amount,
-            &water_config,
+            soil_lateral_flow_ratio,
+            soil_lateral_flow_ratio,
             tile_height_b,
             tile_height_a,
             water_height_b,


Test patch:
diff --git a/emergence_lib/tests/serde_manifests.rs b/emergence_lib/tests/serde_manifests.rs
index a0da820..70cd383 100644
--- a/emergence_lib/tests/serde_manifests.rs
+++ b/emergence_lib/tests/serde_manifests.rs
@@ -77,16 +77,15 @@ fn can_serialize_item_manifest() {
 fn can_serialize_terrain_manifest() {
     // Create a new raw terrain manifest
     let raw_terrain_manifest = RawTerrainManifest {
-        terrain_types: HashMap::from_iter(vec![
-            (
-                "test_terrain".to_string(),
-                TerrainData { walking_speed: 1.0 },
-            ),
-            (
-                "test_terrain2".to_string(),
-                TerrainData { walking_speed: 2.0 },
-            ),
-        ]),
+        terrain_types: HashMap::from_iter(vec![(
+            "test_terrain".to_string(),
+            TerrainData {
+                walking_speed: 1.0,
+                water_capacity: 0.3,
+                water_flow_rate: 0.1,
+                water_evaporation_rate: 0.2,
+            },
+        )]),
     };
 
     // Serialize it