src/plants.rs 2.8 K · 100 lines · raw · history

1 use bevy::prelude::*;
2
3 use crate::{
4 sim::{SimSystems, SimTick},
5 tilemap::PlantLook,
6 world::{Terrain, TileChanged, WorldMap},
7 };
8
9 pub struct PlantsPlugin;
10
11 impl Plugin for PlantsPlugin {
12 fn build(&self, app: &mut App) {
13 app.add_systems(FixedUpdate, grow_plants.in_set(SimSystems::Act));
14 }
15 }
16
17 pub const GROWTH_INTERVAL_TICKS: u64 = 60;
18 const WILD_MIN_FERTILITY: f32 = 0.8;
19
20 pub fn fertility(terrain: Terrain) -> f32 {
21 match terrain {
22 Terrain::Soil => 1.0,
23 Terrain::Grass => 0.8,
24 Terrain::Sand => 0.4,
25 Terrain::Gravel => 0.3,
26 _ => 0.0,
27 }
28 }
29
30 pub fn grow(map: &mut WorldMap, elapsed_ticks: f32, mut on_change: impl FnMut(UVec2)) {
31 for pos in map.growing_plants() {
32 let p = pos.as_ivec2();
33 let Some(plant) = map.plant(p) else {
34 continue;
35 };
36 let Some(ripening) = plant.ripening_ticks() else {
37 continue;
38 };
39 let growth = map.growth(p);
40 if growth >= 1.0 {
41 continue;
42 }
43 let soil = fertility(map.get(p).unwrap_or(Terrain::Grass));
44 let soil = match plant {
45 crate::world::Plant::Crop => soil,
46 _ => soil.max(WILD_MIN_FERTILITY),
47 };
48 let grown = (growth + elapsed_ticks / ripening * soil).min(1.0);
49 map.set_growth(pos, grown);
50 if PlantLook::of(plant, growth) != PlantLook::of(plant, grown) {
51 on_change(pos);
52 }
53 }
54 }
55
56 fn grow_plants(
57 tick: Res<SimTick>,
58 mut map: ResMut<WorldMap>,
59 mut changed: MessageWriter<TileChanged>,
60 ) {
61 if !tick.0.is_multiple_of(GROWTH_INTERVAL_TICKS) {
62 return;
63 }
64 grow(&mut map, GROWTH_INTERVAL_TICKS as f32, |pos| {
65 changed.write(TileChanged(pos));
66 });
67 }
68
69 #[cfg(test)]
70 mod tests {
71 use super::*;
72 use crate::world::Plant;
73
74 fn growth_after(terrain: Terrain, plant: Plant, ticks: f32) -> f32 {
75 let mut map = WorldMap::filled(UVec2::splat(2), terrain);
76 map.set_plant(UVec2::ZERO, Some(plant));
77 grow(&mut map, ticks, |_| {});
78 map.growth(IVec2::ZERO)
79 }
80
81 #[test]
82 fn crops_ripen_faster_on_fertile_soil() {
83 let day = 24.0 * crate::sim::TICKS_PER_HOUR as f32;
84 assert!(
85 growth_after(Terrain::Soil, Plant::Crop, day)
86 > growth_after(Terrain::Sand, Plant::Crop, day)
87 );
88 assert_eq!(growth_after(Terrain::Soil, Plant::Crop, 2.0 * day), 1.0);
89 }
90
91 #[test]
92 fn trees_do_not_grow_and_stage_changes_are_reported() {
93 assert_eq!(growth_after(Terrain::Soil, Plant::Tree, 1e9), 0.0);
94 let mut map = WorldMap::filled(UVec2::splat(2), Terrain::Soil);
95 map.set_plant(UVec2::ZERO, Some(Plant::Crop));
96 let mut changes = 0;
97 grow(&mut map, 1e9, |_| changes += 1);
98 assert_eq!(changes, 1);
99 }
100 }