1
0
mirror of https://github.com/osmarks/ewo3.git synced 2026-05-19 12:02:06 +00:00

'fix' plants somewhat

This commit is contained in:
osmarks
2026-03-14 20:49:04 +00:00
parent b3bf2e1e8e
commit bb2a102573
7 changed files with 239 additions and 111 deletions
Generated
+138 -48
View File
@@ -70,6 +70,15 @@ version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "argh"
version = "0.1.14"
@@ -197,6 +206,12 @@ version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "by_address"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
[[package]]
name = "bytemuck"
version = "1.16.1"
@@ -456,27 +471,6 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "enum-map"
version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9"
dependencies = [
"enum-map-derive",
"serde",
]
[[package]]
name = "enum-map-derive"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -510,7 +504,6 @@ dependencies = [
"anyhow",
"argh",
"bincode",
"enum-map",
"euclid",
"fastrand",
"futures-util",
@@ -524,10 +517,11 @@ dependencies = [
"imgui-winit-support",
"indexmap",
"lazy_static",
"linearize",
"ndarray",
"ndarray-conv",
"noise-functions",
"oklab",
"palette",
"raw-window-handle 0.5.2",
"rayon",
"seahash",
@@ -1005,6 +999,31 @@ dependencies = [
"redox_syscall 0.7.3",
]
[[package]]
name = "linearize"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6e1430c89633736996fd763822abd252e395dbccaaee33be601b4d59678a93e"
dependencies = [
"cfg-if",
"linearize-derive",
"rand 0.8.5",
"rand 0.9.2",
"serde",
"version_check",
]
[[package]]
name = "linearize-derive"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f657db73fbcad5341c5991ddee6c464d4bfd521575c0dc1a47913e0f434defeb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@@ -1278,16 +1297,6 @@ version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666"
[[package]]
name = "oklab"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e35ab3c8efa6bc97d651abe7fb051aebb30c925a450ff6722cf9c797a938cc"
dependencies = [
"fast-srgb8",
"rgb",
]
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -1313,6 +1322,30 @@ dependencies = [
"ttf-parser",
]
[[package]]
name = "palette"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6"
dependencies = [
"approx",
"fast-srgb8",
"palette_derive",
"phf",
]
[[package]]
name = "palette_derive"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30"
dependencies = [
"by_address",
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]]
name = "parking_lot"
version = "0.12.3"
@@ -1342,6 +1375,48 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "phf"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_macros",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"rand 0.8.5",
]
[[package]]
name = "phf_macros"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
@@ -1434,9 +1509,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.85"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
@@ -1473,7 +1548,16 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_core 0.9.5",
]
[[package]]
@@ -1483,7 +1567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
@@ -1495,6 +1579,12 @@ dependencies = [
"getrandom 0.2.15",
]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
[[package]]
name = "raw-window-handle"
version = "0.5.2"
@@ -1569,12 +1659,6 @@ dependencies = [
"bitflags 2.11.0",
]
[[package]]
name = "rgb"
version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
[[package]]
name = "rustfft"
version = "6.4.1"
@@ -1750,6 +1834,12 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "siphasher"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]]
name = "slab"
version = "0.4.9"
@@ -1928,9 +2018,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.49.0"
version = "1.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
dependencies = [
"bytes",
"libc",
@@ -2035,7 +2125,7 @@ dependencies = [
"http",
"httparse",
"log",
"rand",
"rand 0.8.5",
"sha1",
"thiserror 1.0.61",
"utf-8",
@@ -2073,9 +2163,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "version_check"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "virtue"
+2 -2
View File
@@ -28,7 +28,6 @@ smallvec = { version = "1", features = ["serde"] }
argh = "0.1"
ndarray-conv = "0.6.0"
ndarray = "0.17.2"
enum-map = { version = "2.7.3", features = ["serde"] }
imgui = "0.12"
imgui-winit-support = "0.12"
imgui-glow-renderer = "0.12"
@@ -37,7 +36,8 @@ glutin = "0.31"
glutin-winit = "0.4"
raw-window-handle = "0.5"
winit = "0.29"
oklab = "1.1.2"
linearize = { version = "0.1.6", features = ["derive", "serde-1"] }
palette = "0.7.6"
[[bin]]
name = "render"
+10 -10
View File
@@ -5,7 +5,7 @@ use indexmap::IndexMap;
use hecs::Entity;
use serde::{Deserialize, Serialize};
use smallvec::{smallvec, SmallVec};
use enum_map::{Enum, EnumMap};
use linearize::{Linearize, StaticMap};
use crate::map::*;
use crate::plant;
@@ -22,7 +22,7 @@ pub enum NonFungibleItem {
Seed(plant::Genome)
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Enum, Copy)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Linearize, Copy)]
pub enum HealthChangeType {
BluntForce,
Magic,
@@ -187,8 +187,8 @@ impl HealthChangeModifier {
pub struct Health {
pub current: f32,
pub max: f32,
pub health_change_totals: EnumMap<HealthChangeType, f32>,
pub health_change_modifiers: EnumMap<HealthChangeType, HealthChangeModifier>
pub health_change_totals: StaticMap<HealthChangeType, f32>,
pub health_change_modifiers: StaticMap<HealthChangeType, HealthChangeModifier>
}
impl Health {
@@ -204,8 +204,8 @@ impl Health {
Self {
current,
max,
health_change_modifiers: EnumMap::from_fn(|_| HealthChangeModifier::default()),
health_change_totals: EnumMap::from_fn(|_| 0.0)
health_change_modifiers: StaticMap::from_fn(|_| HealthChangeModifier::default()),
health_change_totals: StaticMap::from_fn(|_| 0.0)
}
}
@@ -221,8 +221,8 @@ impl Health {
Self {
current: f32::MAX,
max: f32::MAX,
health_change_modifiers: EnumMap::from_fn(|_| HealthChangeModifier::invulnerable()),
health_change_totals: EnumMap::from_fn(|_| 0.0)
health_change_modifiers: StaticMap::from_fn(|_| HealthChangeModifier::invulnerable()),
health_change_totals: StaticMap::from_fn(|_| 0.0)
}
}
}
@@ -395,10 +395,10 @@ pub struct Plant {
}
impl Plant {
pub fn new(genome: plant::Genome) -> Self {
pub fn new(genome: plant::Genome, size: f32) -> Self {
Self {
genome,
current_size: 0.1,
current_size: size * 0.1,
nutrients_consumed: 0.0,
nutrients_added: 0.0,
water_consumed: 0.0,
+19 -8
View File
@@ -302,7 +302,12 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
if state.ticks % FIELD_DECAY_DELAY == 0 {
state.dynamic_soil_nutrients.for_each_mut(|nutrients| *nutrients *= 0.9999);
} else if state.ticks % FIELD_DECAY_DELAY == 1 {
state.dynamic_groundwater.for_each_mut(|water| *water *= 0.999);
for (pos, water) in state.dynamic_groundwater.iter_mut() {
*water *= 0.999;
if state.map.water[pos] > 0.0 {
*water *= 0.9; // reversion to baseline much faster in active water areas
}
}
} else if state.ticks % FIELD_DECAY_DELAY == 2 {
state.dynamic_soil_nutrients = smooth(&state.dynamic_soil_nutrients, 3);
} else if state.ticks % FIELD_DECAY_DELAY == 3 {
@@ -472,7 +477,7 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
}
if let Some(other) = maybe_other {
// TODO: multiple seeds from one plant with different genomes\
// TODO: multiple seeds from one plant with different hybridizations?
let [plant, other_plant] = state.world.query_disjoint_mut::<&mut Plant, 2>([entity, other]);
let plant = plant?;
let other_plant = other_plant?;
@@ -483,18 +488,23 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
if !state.map.heightmap.in_range(newpos) || state.positions.entities[newpos].is_some() || !hybrid_genome.terrain_valid(&state.map.get_terrain(newpos)) {
continue;
}
let child_size = (plant.genome.initial_size_scale() + other_plant.genome.initial_size_scale()) * 0.5;
buffer.cmd.spawn((
Position::single_tile(newpos, MapLayer::Entities),
Render('+'),
Health::new(1.0, 1.0),
Plant::new(hybrid_genome.clone()),
// TODO: work out more reasonable health/size parameterization, or at least factor this out
Health::new(1.0 + (child_size - 1.0) * 2.0, 1.0 + (child_size - 1.0) * 2.0),
Plant::new(hybrid_genome.clone(), child_size),
NewlyAdded
));
plant.children += 1;
other_plant.children += 1;
// TODO: can this go negative?
plant.current_size -= PLANT_CHILD_COST;
other_plant.current_size -= PLANT_CHILD_COST;
// TODO: gendering?
plant.current_size -= plant.genome.initial_size_scale() * 0.5;
other_plant.current_size -= other_plant.genome.initial_size_scale() * 0.5;
state.metrics.plants_reproduced += 1;
break;
}
@@ -904,12 +914,13 @@ async fn main() -> Result<()> {
let radius = state.map.radius();
let pos = Coord::origin() + sample_range(&mut state.rng, radius);
if genome.base_growth_rate(state.actual_soil_nutrients(pos), state.actual_groundwater(pos), state.baseline_temperature[pos], state.baseline_salt[pos], &state.map.get_terrain(pos)) > 0.2 && !used.contains(&pos) {
let initial_size = genome.initial_size_scale();
batch.push((
Position::single_tile(pos, MapLayer::Entities),
Render('+'),
Health::new(1.0, 1.0),
Health::new(1.0 + (initial_size - 1.0) * 2.0, 1.0 + (initial_size - 1.0) * 2.0),
//ShrinkOnDeath,
Plant::new(genome),
Plant::new(genome, initial_size),
NewlyAdded
));
used.insert(pos);
+26 -18
View File
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::worldgen::TerrainType;
use crate::util::*;
use crate::util::config::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CropType {
@@ -25,6 +26,7 @@ pub struct Genome {
mature_size: f32,
lifespan_multiplier: f32,
reproduction_rate: f32,
initial_size_scale: f32
// TODO number of seeds produced?
// TODO color trait
}
@@ -47,10 +49,10 @@ impl Genome {
}
let temperature_diff = (temperature - sigmoid(self.optimal_temperature)).powf(2.0);
let base = 1.0
- self.nutrient_addition_rate() * 0.16 // nutrient enrichment has a growth tradeoff
- self.water_tolerance * 0.08
- self.temperature_tolerance * 0.07
- self.salt_tolerance.max(0.0) * 0.05;
- self.nutrient_addition_rate() * 0.04 // nutrient enrichment has a growth tradeoff
- self.water_tolerance * 0.11
- self.temperature_tolerance * 0.09
- self.salt_tolerance.max(0.0) * 0.01;
let base = base.max(0.0);
let water_tolerance_coefficient = 13.0 * (1.0 + (-self.water_tolerance).exp());
@@ -89,6 +91,10 @@ impl Genome {
self.nutrient_addition_rate.max(0.0)
}
pub fn initial_size_scale(&self) -> f32 {
self.initial_size_scale.exp()
}
pub fn reproduction_rate(&self) -> f32 {
sigmoid(self.reproduction_rate)
}
@@ -106,10 +112,10 @@ impl Genome {
// Size is something like total mass of a 1m^2 collection of this.
// TODO pick these more precisely.
let (nutrient_addition_rate, optimal_water_level, optimal_temperature, salt_tolerance, lifespan_multiplier, mature_size, reproduction_rate) = match crop_type {
CropType::Grass => (-10.0,-1.0,-0.5, 0.0, 0.5, -1.0, 0.0), // TODO: tie reproduction rate to something else?
CropType::EucalyptusTree => (-10.0, 1.0, 0.0, 0.1, 5.0, 6.0, -4.0),
CropType::BushTomato => (-10.0, 0.0, 1.0, 0.2, 1.5, 1.0, -3.0),
CropType::GoldenWattleTree => ( 2.0, 0.5, 0.2, 0.7, 3.0, 4.0, -4.0),
CropType::Grass => (-10.0,-1.0,-0.5, 0.0, 0.5, 0.0, 0.0), // TODO: tie reproduction rate to something else?
CropType::EucalyptusTree => (-10.0, 1.0, 0.0, 0.6, 5.0, 6.0, -4.0),
CropType::BushTomato => (-10.0, 0.0, 1.0, 1.2, 1.5, 1.0, -3.0),
CropType::GoldenWattleTree => ( 2.0, 0.5, 0.2, 4.0, 3.0, 4.0, -4.0),
};
@@ -123,7 +129,8 @@ impl Genome {
salt_tolerance: normal(rng) + salt_tolerance,
mature_size,
lifespan_multiplier,
reproduction_rate
reproduction_rate,
initial_size_scale: mature_size * 0.5 // TODO
}
}
@@ -136,15 +143,16 @@ impl Genome {
if self.crop_type != other.crop_type { return None }
Some(Genome {
crop_type: self.crop_type,
nutrient_addition_rate: (self.nutrient_addition_rate + other.nutrient_addition_rate) / 2.0 + normal_scaled(rng, 0.0, 0.03),
optimal_water_level: (self.optimal_water_level + other.optimal_water_level) / 2.0 + normal_scaled(rng, 0.0, 0.03),
optimal_temperature: (self.optimal_temperature + other.optimal_temperature) / 2.0 + normal_scaled(rng, 0.0, 0.03),
temperature_tolerance: (self.temperature_tolerance + other.temperature_tolerance) / 2.0 + normal_scaled(rng, 0.0, 0.2),
water_tolerance: (self.water_tolerance + other.water_tolerance) / 2.0 + normal_scaled(rng, 0.0, 0.2),
salt_tolerance: (self.salt_tolerance + other.salt_tolerance) / 2.0 + normal_scaled(rng, 0.0, 0.2),
mature_size: (self.mature_size + other.mature_size) / 2.0 + normal_scaled(rng, 0.0, 0.1),
lifespan_multiplier: (self.lifespan_multiplier + other.lifespan_multiplier) / 2.0 + normal_scaled(rng, 0.0, 0.1),
reproduction_rate: (self.reproduction_rate + other.reproduction_rate) / 2.0 + normal_scaled(rng, 0.0, 0.05),
nutrient_addition_rate: (self.nutrient_addition_rate + other.nutrient_addition_rate) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.03),
optimal_water_level: (self.optimal_water_level + other.optimal_water_level) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.03),
optimal_temperature: (self.optimal_temperature + other.optimal_temperature) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.03),
temperature_tolerance: (self.temperature_tolerance + other.temperature_tolerance) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.1),
water_tolerance: (self.water_tolerance + other.water_tolerance) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.02),
salt_tolerance: (self.salt_tolerance + other.salt_tolerance) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.02),
mature_size: (self.mature_size + other.mature_size) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.1),
lifespan_multiplier: (self.lifespan_multiplier + other.lifespan_multiplier) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.1),
reproduction_rate: (self.reproduction_rate + other.reproduction_rate) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.05),
initial_size_scale: (self.initial_size_scale + other.initial_size_scale) / 2.0 + EVOLUTION_RATE * normal_scaled(rng, 0.0, 0.1),
})
}
}
+39 -20
View File
@@ -3,12 +3,6 @@ extern crate test;
use anyhow::{anyhow, Context, Result};
use argh::FromArgs;
use ewo3::components::{Health, MapLayer, Plant, Position, PositionIndex, Render};
use ewo3::map::*;
use ewo3::save::SavedGame;
use ewo3::world_serde;
use ewo3::worldgen::*;
use ewo3::util::config::*;
use glow::HasContext;
use glutin::config::ConfigTemplateBuilder;
use glutin::context::{ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentContext};
@@ -21,12 +15,22 @@ use ndarray::{Array1, Array2, Axis};
use hecs::World;
use image::{ImageBuffer, Rgb};
use imgui_winit_support::{HiDpiMode, WinitPlatform};
use palette::IntoColor;
use std::collections::HashMap;
use std::num::NonZeroU32;
use std::time::Instant;
use winit::event::{Event, WindowEvent};
use winit::event_loop::EventLoopBuilder;
use raw_window_handle::HasRawWindowHandle;
use linearize::{Linearize, LinearizeExt};
use ewo3::components::{Health, MapLayer, Plant, Position, PositionIndex, Render};
use ewo3::map::*;
use ewo3::plant::CropType;
use ewo3::save::SavedGame;
use ewo3::world_serde;
use ewo3::worldgen::*;
use ewo3::util::config::*;
#[derive(FromArgs)]
/// Render world/debug fields from generated terrain or a saved game.
@@ -130,19 +134,23 @@ define_fields! {
DynamicSoil => "dynamic_soil",
EntityHealth => "health",
EntityHealthFraction => "health_fraction",
PlantsType => "plant_type"
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Linearize)]
enum ColorSpace {
Rgb,
Oklab,
Hsv
}
// TODO: do this better
impl ColorSpace {
fn parse(s: &str) -> Self {
match s {
"oklab" => Self::Oklab,
"hsv" => Self::Hsv,
_ => Self::Rgb,
}
}
@@ -151,6 +159,7 @@ impl ColorSpace {
match self {
ColorSpace::Rgb => "rgb",
ColorSpace::Oklab => "oklab",
ColorSpace::Hsv => "hsv",
}
}
}
@@ -180,6 +189,7 @@ struct RenderData {
dynamic_soil: Map<f32>,
entity_health: Map<f32>,
entity_health_fraction: Map<f32>,
plants_type: Map<f32>,
}
struct RenderedImage {
@@ -222,6 +232,7 @@ fn sample_field(field: Field, position: Coord, data: &RenderData) -> f32 {
Field::DynamicSoil => data.dynamic_soil[position],
Field::EntityHealth => data.entity_health[position],
Field::EntityHealthFraction => data.entity_health_fraction[position],
Field::PlantsType => data.plants_type[position],
}
}
@@ -242,14 +253,13 @@ fn field_range(field: Option<Field>, data: &RenderData) -> (f32, f32) {
}
fn to_rgb(c1: f32, c2: f32, c3: f32, color_space: ColorSpace) -> [u8; 3] {
let (r, g, b) = match color_space {
ColorSpace::Rgb => (c1, c2, c3),
ColorSpace::Oklab => {
let rgb = oklab::oklab_to_srgb_f32(oklab::Oklab { l: c1.clamp(0.0, 1.0), a: c2 * 1.0 - 0.5, b: c3 * 1.0 - 0.5 });
(rgb.r, rgb.g, rgb.b)
}
let transformed: palette::Srgb = match color_space {
ColorSpace::Rgb => palette::Srgb::from_components((c1, c2, c3)).into_color(),
ColorSpace::Oklab => palette::Oklab::from_components((c1.clamp(0.0, 1.0), c2 * 1.0 - 0.5, c3 * 1.0 - 0.5)).into_color(),
ColorSpace::Hsv => palette::Hsv::from_components((c1 * 360.0, c2, c3)).into_color(),
};
[(r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8]
[(transformed.red * 255.0) as u8, (transformed.green * 255.0) as u8, (transformed.blue * 255.0) as u8]
}
fn build_derived_data(
@@ -287,6 +297,7 @@ fn build_derived_data(
let mut plants = Map::new(world.radius, 0.0f32);
let mut plants_age = Map::new(world.radius, 0.0f32);
let mut plants_lifespan_fraction = Map::new(world.radius, 0.0f32);
let mut plants_type = Map::new(world.radius, 0.0f32);
for (position, plant) in ecs_world.query::<(&Position, &Plant)>().iter() {
let pos = position.head();
if !plants.in_range(pos) {
@@ -304,10 +315,17 @@ fn build_derived_data(
}
plants_age[pos] = plant.age as f32;
plants_lifespan_fraction[pos] = plant.age as f32 / (plant.genome.lifespan() * PLANT_LIFESPAN_SCALE);
match plant.genome.crop_type {
CropType::Grass => plants_type[pos] = 1.0f32,
CropType::BushTomato => plants_type[pos] = 2.0f32,
CropType::GoldenWattleTree => plants_type[pos] = 3.0f32,
CropType::EucalyptusTree => plants_type[pos] = 0.0f32,
}
}
let mut entity_health = Map::new(world.radius, 0.0f32);
let mut entity_health_fraction = Map::new(world.radius, 0.0f32);
for (position, health) in ecs_world.query::<(&Position, &Health)>().iter() {
let pos = position.head();
if !plants.in_range(pos) {
@@ -332,6 +350,7 @@ fn build_derived_data(
dynamic_soil: dynamic_soil_nutrients,
entity_health,
entity_health_fraction,
plants_type,
}
}
@@ -781,10 +800,10 @@ fn run_window(data: RenderData, mut settings: RenderSettings) -> Result<()> {
settings.normalize = normalize;
}
let mut cs_idx = if settings.color_space == ColorSpace::Rgb { 0 } else { 1 };
let cs_labels = ["rgb", "oklab"];
if ui.combo_simple_string("Color space", &mut cs_idx, &cs_labels) {
settings.color_space = if cs_idx == 0 { ColorSpace::Rgb } else { ColorSpace::Oklab };
let mut cs_idx = settings.color_space.linearize();
// TODO this should be static
if ui.combo_simple_string("Color space", &mut cs_idx, &ColorSpace::variants().map(|x| x.name()).collect::<Vec<&'static str>>()) {
settings.color_space = ColorSpace::from_linear(cs_idx).unwrap();
}
if pca_mode {
@@ -969,10 +988,10 @@ fn run_window(data: RenderData, mut settings: RenderSettings) -> Result<()> {
let color = imgui::ImColor32::from_rgba_f32s(1.0, 1.0, 1.0, 1.0);
let draw = ui.get_window_draw_list();
draw.add_line([cx - len, cy], [cx + len, cy], color)
.thickness(3.0)
.thickness(2.0)
.build();
draw.add_line([cx, cy - len], [cx, cy + len], color)
.thickness(3.0)
.thickness(2.0)
.build();
} else {
ui.text("hover map to zoom");
+5 -5
View File
@@ -18,9 +18,9 @@ pub mod config {
pub const PLANT_TICK_DELAY: u64 = 128;
pub const FIELD_DECAY_DELAY: u64 = 100;
pub const PLANT_GROWTH_SCALE: f32 = 0.01;
pub const SOIL_NUTRIENT_CONSUMPTION_RATE: f32 = 0.5;
pub const SOIL_NUTRIENT_FIXATION_RATE: f32 = 0.002;
pub const WATER_CONSUMPTION_RATE: f32 = 0.1;
pub const SOIL_NUTRIENT_CONSUMPTION_RATE: f32 = 0.8;
pub const SOIL_NUTRIENT_FIXATION_RATE: f32 = 0.1;
pub const WATER_CONSUMPTION_RATE: f32 = 0.05;
pub const PLANT_IDLE_WATER_CONSUMPTION_OFFSET: f32 = 0.2;
pub const PLANT_DIEOFF_THRESHOLD: f32 = 0.3;
pub const PLANT_DIEOFF_RATE: f32 = 0.2;
@@ -32,8 +32,8 @@ pub mod config {
pub const SOIL_NUTRIENT_DECOMP_RETURN_RATE: f32 = 1.0 / SOIL_NUTRIENT_CONSUMPTION_RATE * 0.6;
pub const PLANT_LIFESPAN_SCALE: f32 = 1.0 / PLANT_GROWTH_SCALE;
pub const PLANT_REPRODUCTION_ATTEMPT_COST: f32 = 0.01;
pub const PLANT_CHILD_COST: f32 = 0.05; // TODO genome parameter, tie to initial size
pub const INITIAL_PLANTS: usize = 262144;
pub const INITIAL_PLANTS: usize = 131072;
pub const EVOLUTION_RATE: f32 = 0.1;
// Runtime game logic (misc)
pub const VIEW: i32 = 15;
pub const RANDOM_DESPAWN_INV_RATE: u64 = 4000;