From a0f18df9efd5be1ef5f994d9ed5eba33bc4758f7 Mon Sep 17 00:00:00 2001 From: Steve Biedermann Date: Sun, 11 Oct 2026 17:48:35 +0200 Subject: [PATCH] feat(ui): expose life stages and fertility settings Show each person's life stage, age, and partner in the inspector. Add per-stage fertility toggles and document the life cycle and birth conditions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 2 ++ ui/src/lib.rs | 29 +++++++++++++++++++++++++++++ ui/src/model.rs | 19 ++++++++++++++----- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5e6ed5f..27b7f70 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Living simulation entities (people, rabbits, and wolves) run as Picard actors. T The **Calendar and life-stage durations** panel configures ticks per day and the number of days, weeks, and months in larger calendar units. Each human life stage has an inclusive minimum/maximum duration in in-game days; the world samples within that range when a person enters the stage. These settings are stored with the world snapshot. +New people, including babies, start as toddlers and age through child, teen, young adult, adult, older adult, elderly, and death. The starting village is seeded with young and mature adults so the simulation can work and form the first partnerships immediately. Unpartnered people from young adulthood onward can form a single partnership with a nearby eligible person. Each partnership has up to one birth attempt per calendar month, with a 20% chance of having a toddler when both partners are in stages marked fertile, the stockpile has enough food, and housing capacity permits it. Young adult and adult stages are fertile by default; fertility can be changed per stage in the calendar panel. Partnerships are Picard actors as well as people and animals. + The UI is built automatically as part of `cargo run` and embedded in the server binary. There is no separate manual UI build step. ## Getting Started diff --git a/ui/src/lib.rs b/ui/src/lib.rs index dad82aa..c5589b4 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -221,6 +221,7 @@ fn App() -> impl IntoView { label="max days" setting=max_setting /> + } }) @@ -337,6 +338,34 @@ fn SettingInput( } } +#[component] +fn StageFertility(state: RwSignal>, stage: String) -> impl IntoView { + let stage_for_change = stage.clone(); + view! { + + } +} + #[component] fn PersonEditor(p: Person) -> impl IntoView { let id = p.0; diff --git a/ui/src/model.rs b/ui/src/model.rs index 38666c6..a72fc35 100644 --- a/ui/src/model.rs +++ b/ui/src/model.rs @@ -24,6 +24,7 @@ pub struct Stock { pub struct StageRange { pub min_days: u32, pub max_days: u32, + pub fertile: bool, } #[derive(Deserialize, Clone)] @@ -43,13 +44,14 @@ pub struct Calendar { impl Calendar { pub fn setting(&self, name: &str) -> Option { - match name { + match name.strip_prefix("calendar.").unwrap_or(name) { "ticks_per_day" => Some(self.ticks_per_day), "days_per_week" => Some(self.days_per_week.into()), "weeks_per_month" => Some(self.weeks_per_month.into()), "months_per_year" => Some(self.months_per_year.into()), _ => { - let (stage, bound) = name.split_once('.')?; + let rest = name.strip_prefix("stage.").unwrap_or(name); + let (stage, bound) = rest.split_once('.')?; let range = match stage { "toddler" => &self.toddler, "child" => &self.child, @@ -63,6 +65,7 @@ impl Calendar { match bound { "min_days" => Some(range.min_days.into()), "max_days" => Some(range.max_days.into()), + "fertile" => Some(range.fertile.into()), _ => None, } } @@ -85,6 +88,9 @@ impl Calendar { "elderly" => &self.elderly, _ => return None, }; + if bound == "fertile" { + return Some(format!("stage {stage} fertile {}", u8::from(value != 0))); + } let (min_days, max_days) = match bound { "min_days" => (value.min(u32::MAX as u64) as u32, range.max_days), "max_days" => (range.min_days, value.min(u32::MAX as u64) as u32), @@ -94,7 +100,7 @@ impl Calendar { } } -/// id, x, y, action, hunger, food, wood, stone +/// id, x, y, action, hunger, food, wood, stone, life stage, age in days, partner id #[derive(Deserialize, Clone)] pub struct Person( pub u32, @@ -105,6 +111,9 @@ pub struct Person( pub u32, pub u32, pub u32, + pub String, + pub u64, + pub Option, ); /// x, y, energy @@ -289,8 +298,8 @@ pub fn describe(sel: Sel, s: &State, t: &Terrain) -> Vec { )); for p in &people { out.push(format!( - "- Person #{}: {}, hunger {}, carrying food {} wood {} stone {}", - p.0, p.3, p.4, p.5, p.6, p.7 + "- Person #{}: {}, stage {}, age {} days, hunger {}, partner {:?}, carrying food {} wood {} stone {}", + p.0, p.3, p.8, p.9, p.4, p.10, p.5, p.6, p.7 )); } if rabbits.len() + wolves.len() <= 6 {