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>
This commit is contained in:
parent
2cb4d4e626
commit
a0f18df9ef
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ fn App() -> impl IntoView {
|
|||
label="max days"
|
||||
setting=max_setting
|
||||
/>
|
||||
<StageFertility state=state stage=stage.to_string() />
|
||||
</div>
|
||||
}
|
||||
})
|
||||
|
|
@ -337,6 +338,34 @@ fn SettingInput(
|
|||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn StageFertility(state: RwSignal<Option<State>>, stage: String) -> impl IntoView {
|
||||
let stage_for_change = stage.clone();
|
||||
view! {
|
||||
<label class="setting">
|
||||
<input
|
||||
type="checkbox"
|
||||
prop:checked=move || {
|
||||
let setting = format!("stage.{stage}.fertile");
|
||||
state.with(|s| {
|
||||
s.as_ref()
|
||||
.and_then(|s| s.calendar.setting(&setting))
|
||||
.unwrap_or(0)
|
||||
!= 0
|
||||
})
|
||||
}
|
||||
on:change=move |event| {
|
||||
api::edit(format!(
|
||||
"stage {stage_for_change} fertile {}",
|
||||
u8::from(event_target_checked(&event))
|
||||
));
|
||||
}
|
||||
/>
|
||||
" fertile"
|
||||
</label>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn PersonEditor(p: Person) -> impl IntoView {
|
||||
let id = p.0;
|
||||
|
|
|
|||
|
|
@ -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<u64> {
|
||||
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<u32>,
|
||||
);
|
||||
|
||||
/// x, y, energy
|
||||
|
|
@ -289,8 +298,8 @@ pub fn describe(sel: Sel, s: &State, t: &Terrain) -> Vec<String> {
|
|||
));
|
||||
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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue