1use banished_formats::names::NameDatabase;
8
9use crate::people::names::citizen_name;
10use crate::random::Random;
11
12#[derive(Debug, Clone, Copy)]
14pub struct CitizenRules {
15 pub adult_age: f32,
18 pub start_age: (f32, f32),
21 pub lifespan: (f32, f32),
24 pub education: i32,
27}
28
29#[derive(Debug, Clone, PartialEq)]
31pub struct Citizen {
32 pub name: String,
35 pub male: bool,
38 pub age: f32,
41 pub lifespan: f32,
44 pub appearance: u32,
47 pub educated: bool,
50 pub education: i32,
52 pub position: [f32; 3],
54 pub spouse: Option<usize>,
57 pub parent: Option<usize>,
61 pub school: Option<usize>,
64 pub alive: bool,
66}
67
68impl Citizen {
69 pub fn is_adult(&self, rules: &CitizenRules) -> bool {
71 self.age >= rules.adult_age
72 }
73}
74
75pub struct Households<'a> {
77 pub rules: CitizenRules,
79 pub male_names: &'a NameDatabase,
81 pub female_names: &'a NameDatabase,
83 pub spread: f32,
86 pub start: [f32; 3],
90 pub citizens: Vec<Citizen>,
92}
93
94const HALF: f32 = 0.5;
96
97impl Households<'_> {
98 fn create(&mut self, rng: &mut Random, age: f32, female: bool, educated: bool) -> usize {
100 let child = age < self.rules.adult_age;
103 let educated = educated && !child;
104 let dy = rng.rand_float_tol();
105 let dx = rng.rand_float_tol();
106 let position = [
107 dx * self.spread + self.start[0],
108 dy * self.spread + self.start[1],
109 self.start[2] + 0.0,
110 ];
111 let male = !female;
112 let db = if male {
113 self.male_names
114 } else {
115 self.female_names
116 };
117 let name = citizen_name(db, rng);
118 let a = rng.rand();
119 let b = rng.rand();
120 let appearance = a.wrapping_add(b) & 7;
121 let lifespan = rng.rand_float_tol() * self.rules.lifespan.1 + self.rules.lifespan.0;
122 self.citizens.push(Citizen {
123 name,
124 male,
125 age,
126 lifespan,
127 appearance,
128 educated,
129 education: if educated { self.rules.education } else { 0 },
130 position,
131 spouse: None,
132 parent: None,
133 school: None,
134 alive: true,
135 });
136 self.citizens.len() - 1
137 }
138
139 pub fn create_all(&mut self, rng: &mut Random, rows: &[(bool, i32)]) {
144 let (lo, hi) = self.rules.start_age;
145 let mut female_child = true;
146 for &(educated, extra) in rows {
147 let age = rng.rand_float() * (hi * HALF - lo) + lo;
148 let husband = self.create(rng, age, false, educated);
149 let age = rng.rand_float() * (hi * HALF - lo) + lo;
150 let wife = self.create(rng, age, true, educated);
151 self.citizens[husband].spouse = Some(wife);
152 self.citizens[wife].spouse = Some(husband);
153 let children = rng.rand_below((extra + 1) as u32);
154 for _ in 0..children {
155 let age = rng.rand_float() * self.rules.adult_age;
156 let child = self.create(rng, age, female_child, false);
157 female_child = !female_child;
158 self.citizens[child].parent = Some(wife);
159 }
160 }
161 }
162}
163
164pub fn newborn(
169 rng: &mut Random,
170 rules: &CitizenRules,
171 male_names: &NameDatabase,
172 female_names: &NameDatabase,
173 mother: &Citizen,
174 male: bool,
175) -> Citizen {
176 let name = citizen_name(if male { male_names } else { female_names }, rng);
177 let a = rng.rand();
178 let b = rng.rand();
179 let appearance = a.wrapping_add(b) & 7;
180 let lifespan = rng.rand_float_tol() * rules.lifespan.1 + rules.lifespan.0;
181 Citizen {
182 name,
183 male,
184 age: 0.0,
185 lifespan,
186 appearance,
187 educated: false,
188 education: 0,
189 position: mother.position,
190 spouse: None,
191 parent: None,
192 school: None,
193 alive: true,
194 }
195}
196
197pub fn population(citizens: &[Citizen], rules: &CitizenRules) -> (u32, u32, u32) {
201 let (mut adults, mut students, mut children) = (0, 0, 0);
202 for c in citizens.iter().filter(|c| c.alive) {
203 if c.age < rules.adult_age || c.school.is_some() {
204 if c.school.is_none() {
205 children += 1;
206 } else {
207 students += 1;
208 }
209 } else {
210 adults += 1;
211 }
212 }
213 (adults, students, children)
214}