banished_game/behaviour/agents.rs
1//! The citizens' CharacterComponent and CitizenAIComponent run together each simulation
2//! iteration. See docs/CITIZEN-AI.md.
3//!
4//! Order per iteration: animations advance, every character runs its actions and moves, "sequence
5//! finished" messages run the AI again with zero time, then the AI updates.
6//!
7//! The citizens' scheduled components (CitizenComponent, CitizenAIComponent and the
8//! CharacterComponent's notify) run for the citizens bucket 1 visited this iteration, with the
9//! time since each one's last visit; so do the sites' state machines, fires, blight, hunts,
10//! attendance, residences and piles in bucket 3 (`town::Env::visits`).
11//!
12//! Deviation: these run after the bucket pass (system by system, each in visiting order)
13//! instead of interleaved with it entity by entity, so random draws can come in another order.
14//!
15//! This file holds the agents' state and set-up; the rest is split by topic: `update` (the
16//! iteration), `thinking` (the AI), `lives`, `commands`, `paths` and `views`.
17//!
18//! Original: `AnimationManager::Update`; the entity manager's bit-`0x04` components;
19//! `FUN_140114810` (sequence finished); `FUN_1400c9560` (the buckets).
20
21mod commands;
22mod lives;
23mod paths;
24mod thinking;
25mod update;
26mod views;
27pub use views::entity_views;
28use views::{refresh_views, storage_of};
29
30use banished_formats::game::CharacterDescription;
31use banished_formats::types::AnimationGroup;
32
33use crate::behaviour::activities::{loiter, EntityView, StorageGeometry, TownView};
34use crate::behaviour::ai::{activity, CitizenAi, CitizenView, Thresholds};
35use crate::behaviour::character::{Action, Character, Component, Message, MoveTo, World};
36use crate::nature::seeding::{Cells, Placed, Template};
37use crate::pathfind::{regions, Pathfinder, WALKABLE};
38use crate::people::citizens::Citizen;
39use crate::random::Random;
40use crate::scheduler::ticks;
41use crate::sectors::SectorGraph;
42use crate::terrain::{CellType, HeightGrid};
43use crate::town::{Env, Sim};
44use crate::work::stock::{find_storage, RawMaterial, Storage, StorageSite};
45
46/// `DAT_140178818`: children's model and walk scale (`FUN_14011a700`).
47const CHILD_SCALE: f32 = 0.66;
48
49/// `DAT_1401788fc`: cooldown set by the fetch-tool and fetch-clothing activities.
50const FETCH_COOLDOWN: f32 = 75.0;
51
52/// The CitizenDescription values the AI reads.
53#[derive(Debug, Clone, Copy, Default)]
54pub struct CitizenRulesAi {
55 /// The thresholds of the citizen's needs.
56 pub thresholds: Thresholds,
57 // +0x6c; +0xa4
58 /// The starting health.
59 pub health: f32,
60 /// The starting happiness.
61 pub happiness: f32,
62 // +0xbc
63 /// Loiter search range.
64 pub loiter_range: i32,
65 // +0x130
66 /// How long clothing lasts, in seconds (the citizen description's value multiplied by 300).
67 ///
68 /// Original: `FUN_140117730`.
69 pub clothing_duration: f32,
70 /// The highest health.
71 pub health_max: f32,
72 /// Midpoint for food, quarter-point for cold status warnings.
73 ///
74 /// Original: `FUN_140118340`.
75 pub hunger_limit: f32,
76 /// The cold level of the cold warning.
77 pub cold_limit: f32,
78 /// Whether a citizen without a workplace shows no job warning, when no professions are loaded.
79 pub profession_allows_idle: bool,
80 // +0x110
81 /// CitizenDescription: the tool type CitizenUI calls "Iron" (others are "Steel").
82 pub tool_grade: i32,
83 // CitizenDescription +0xfc, +0x100, +0x104 (ints) and +0x108, +0x10c (°F); used by FUN_1400fc3c0.
84 /// The rank of work a citizen takes at once, before it has waited.
85 pub work_near: i32,
86 /// The rank of work a citizen takes after it has waited the longest.
87 pub work_far: i32,
88 /// The longest wait, in months (of 300 seconds).
89 pub work_minutes: i32,
90 /// The rank of work a citizen always takes.
91 pub work_ok: i32,
92 /// The temperature at or above which a citizen takes any work in range.
93 pub work_cold: f32,
94 // +0x114
95 /// CitizenDescription: work duration factor without a tool.
96 ///
97 /// Original: `FUN_1400cebf0`.
98 pub no_tool_factor: f32,
99 // +0x118
100 /// The education a citizen counts as educated with.
101 pub education: i32,
102 // +0xf8
103 /// How far a firefighter looks for water.
104 ///
105 /// Original: `FUN_14008d6a0`.
106 pub water_distance: i32,
107}
108
109/// Citizen fields read/written by equipment activities and Citizen messages 1/6/7.
110#[derive(Debug, Clone, Default)]
111pub struct CitizenState {
112 /// The health.
113 pub health: f32,
114 /// The happiness.
115 pub happiness: f32,
116 /// The hunger.
117 pub hunger: f32,
118 /// The cold.
119 pub cold: f32,
120 /// Whether the citizen is sick.
121 pub sick: bool,
122 /// The work it is assigned, if any (*inferred*).
123 pub work: Option<usize>,
124 // +0xf0; +0x280
125 /// StatusIconComponent (Citizen points to that component).
126 pub status: u32,
127 // +0xa8
128 /// The tool level shown in the citizen window.
129 pub tool_status: i32,
130 // +0xac
131 /// The uses left on its tool.
132 pub tool_uses: i32,
133 // +0x1e0
134 /// The clothing level shown in the citizen window.
135 pub clothing_status: i32,
136 // +0x1e4
137 /// The clothing level: 0 none, 1 or 2.
138 pub clothing: i32,
139 // +0x1e8
140 /// The time its clothing lasts.
141 pub clothing_remaining: i64,
142 // +0x54
143 /// Flags of health events.
144 pub health_events: u32,
145 // +0x88
146 /// Status flags.
147 pub flags: u32,
148 // +0xb0
149 /// The supply errand it runs for its home: 1 food, 2 firewood.
150 pub supply_activity: i32,
151 // +0x30
152 /// The id of the work it does (−1 none).
153 pub job: i32,
154 // +0x238
155 /// The home, if any.
156 pub home: Option<usize>,
157 /// What it carries.
158 pub carried: Storage,
159 // +0x1f0
160 /// Profession (index into the profession list).
161 pub profession: usize,
162 // +0x248
163 /// Workplace entity.
164 pub workplace: Option<usize>,
165 /// The tool ShowTool attached (−1 none, −2 carried goods).
166 pub shown_tool: i32,
167 // +0x240
168 /// Family slot in the home's ResidenceComponent (−1 none).
169 pub home_slot: i32,
170 // +0x4c
171 /// Pregnancy (positive, counting down) or the wait after a birth (negative).
172 pub reproduction: f32,
173 // +0x58, +0x90
174 /// Time towards the next health step.
175 pub health_timer: i64,
176 /// Time towards the next happiness step.
177 pub happiness_timer: i64,
178 // +0x98
179 /// Every kind of food eaten.
180 pub foods: Vec<usize>,
181 // +0x260
182 /// The children and other dependents, in ring order.
183 pub dependents: Vec<usize>,
184 // +0x218
185 /// Time attending school.
186 pub school_time: i64,
187 // +0x208
188 /// The school's AttendanceComponent entity (`Citizen::school` mirrors it).
189 pub school: Option<usize>,
190 // +0x228, +0x230
191 /// The doctor's AttendanceComponent entity.
192 pub doctor: Option<usize>,
193 /// The chapel's AttendanceComponent entity.
194 pub priest: Option<usize>,
195 // +0x68
196 /// The disease (index into the disease list).
197 pub disease: Option<usize>,
198 // +0x70
199 /// The illness left while sick.
200 pub disease_left: i64,
201 /// The immunity left after an illness.
202 pub immunity: i64,
203 // +0x78
204 /// Time to the next spread attempt.
205 pub spread: i64,
206 // +0x60
207 /// Time since the last disease check.
208 pub disease_clock: i64,
209 // +0x84
210 /// The work count the happiness compares with `_workForHappiness`; never written in 1.0.7, so
211 /// 0.
212 pub field_84: i32,
213 // +0x220
214 /// Has had a school place.
215 pub schooled: bool,
216 // +0x1f8
217 /// The profession whose work it had an accident at.
218 ///
219 /// Original: `FUN_14011bb20`.
220 pub accident: Option<usize>,
221 // +0x200
222 /// Time since the last injury check.
223 pub accident_timer: i64,
224}
225
226impl CitizenState {
227 /// The state of a new citizen.
228 ///
229 /// Original: `FUN_140119c40`, set-up `FUN_140119520`.
230 pub fn newborn(rules: &crate::people::life::LifeRules) -> CitizenState {
231 CitizenState {
232 job: -1,
233 shown_tool: -1,
234 home_slot: -1,
235 health: rules.f(0x6c),
236 happiness: rules.f(0xa4),
237 ..Default::default()
238 }
239 }
240}
241
242impl CitizenState {
243 /// Handle a message about equipment (a tool or clothing given, worn out or used).
244 ///
245 /// Original: the equipment branches of `FUN_140117730` and `FUN_1401152b0`.
246 pub fn message(&mut self, m: Message, rules: CitizenRulesAi) {
247 match m.message {
248 1 => {
249 crate::people::status::set_icon(&mut self.status, 4, false, false);
250 self.tool_uses = self.tool_uses.wrapping_add(m.a);
251 self.tool_status = m.a;
252 }
253 6 | 7 => {
254 self.clothing = m.message - 5;
255 self.clothing_status = self.clothing;
256 self.clothing_remaining = ticks(rules.clothing_duration);
257 self.flags |= 0x800;
258 }
259 8 => self.flags |= 0x40000,
260 _ => {}
261 }
262 }
263}
264
265struct TownStock<'a> {
266 materials: &'a [RawMaterial],
267 storages: &'a mut [Storage],
268 sites: &'a [StorageSite],
269}
270
271/// The citizens' agents: each citizen's AI and character, and the state the iteration needs.
272pub struct Agents {
273 /// The citizens' characters.
274 pub characters: Vec<Character>,
275 /// The citizens' AI.
276 pub ais: Vec<CitizenAi>,
277 /// The citizens' states.
278 pub states: Vec<CitizenState>,
279 /// The CitizenDescription for the CitizenComponent's update.
280 pub life: crate::people::life::LifeRules,
281 // +0xa0
282 /// The living citizens, in activation order.
283 pub registry: Vec<usize>,
284 /// Log entries the citizens asked for since the caller last took them.
285 pub events: Vec<crate::people::life::LifeEvent>,
286 // GameGlobal +0xa8
287 /// `Game/Disease/Diseases.rsc` (`GameGlobal`).
288 pub diseases: Vec<banished_formats::game::Disease>,
289 // +0x1a8
290 /// The town's disease level.
291 pub town_disease: f32,
292 /// The character description new citizens get.
293 desc: CharacterDescription,
294 /// A new citizen's carried storage.
295 carried: Storage,
296 /// Citizens queued for deletion this iteration (`FUN_14011c2a0`), with the reason.
297 dying: Vec<(usize, i32)>,
298 // +0x50
299 /// The laborer profession's carry amount (what home stocking carries at least).
300 pub carry: i32,
301 sectors: SectorGraph,
302 pathfinder: Pathfinder,
303 regions: Vec<u16>,
304 region_types: Vec<CellType>,
305 /// The [`Cells::types_revision`] `region_types` was last brought up to.
306 types_seen: u64,
307 /// How far `Cells::sector_marks` has been taken in.
308 marks_seen: usize,
309 rules: CitizenRulesAi,
310}
311
312impl Agents {
313 /// New agents with no citizens.
314 pub fn new(
315 citizens: &[Citizen],
316 cells: &Cells,
317 desc: CharacterDescription,
318 rules: CitizenRulesAi,
319 carried: Storage,
320 scale: impl Fn(&Citizen) -> f32,
321 ) -> Agents {
322 let mut states: Vec<CitizenState> = citizens
323 .iter()
324 .map(|_| CitizenState {
325 job: -1,
326 shown_tool: -1,
327 home_slot: -1,
328 health: rules.health,
329 happiness: rules.happiness,
330 carried: carried.clone(),
331 ..Default::default()
332 })
333 .collect();
334 for (s, c) in states.iter_mut().zip(citizens) {
335 s.school = c.school;
336 }
337 // `FUN_14011bd70` put each child in its guardian's ring.
338 for (i, c) in citizens.iter().enumerate() {
339 if let Some(g) = c.parent {
340 states[g].dependents.push(i);
341 }
342 }
343 Agents {
344 characters: citizens
345 .iter()
346 .map(|c| Character::new(desc, c.position, scale(c)))
347 .collect(),
348 ais: citizens.iter().map(|_| CitizenAi::default()).collect(),
349 states,
350 life: crate::people::life::LifeRules::default(),
351 registry: (0..citizens.len()).collect(),
352 events: Vec::new(),
353 diseases: Vec::new(),
354 town_disease: 0.0,
355 desc,
356 carried,
357 carry: 0,
358 dying: Vec::new(),
359 sectors: SectorGraph::new(cells.size, &cells.types),
360 pathfinder: Pathfinder::new(cells.size, &cells.types),
361 regions: regions(cells.size, &cells.types),
362 region_types: cells.types.clone(),
363 types_seen: cells.types_revision,
364 marks_seen: cells.sector_marks.len(),
365 rules,
366 }
367 }
368
369 /// The AI's rules, from the citizen description.
370 pub fn rules(&self) -> CitizenRulesAi {
371 self.rules
372 }
373
374 /// The work and construction context over these citizens and `env`.
375 fn sim<'s, 't>(
376 &'s mut self,
377 citizens: &[Citizen],
378 env: &'s mut Env<'_, 't>,
379 groups: &'s [Option<&'s AnimationGroup>],
380 adult: &'s [bool],
381 entities: &'s [EntityView],
382 ) -> (Sim<'s, 't>, &'s mut crate::work::jobs::Jobs) {
383 let _ = citizens;
384 let c = &mut *env.construction;
385 (
386 Sim {
387 characters: &mut self.characters,
388 states: &mut self.states,
389 groups,
390 adult,
391 rules: self.rules,
392 size: env.cells.size,
393 cells: &mut *env.cells,
394 regions: &self.regions,
395 sectors: &mut self.sectors,
396 terrain: env.terrain,
397 placed: &mut *env.placed,
398 templates: env.templates,
399 entities,
400 storages: &mut *env.storages,
401 storage_sites: env.sites,
402 materials: env.materials,
403 rng: &mut *env.rng,
404 stock: env.stock,
405 temperature: env.temperature,
406 professions: &c.professions,
407 workforce: &mut c.workforce,
408 site_templates: &c.templates,
409 sites: &mut c.sites,
410 homes: &mut c.homes,
411 attendance: &mut c.attendance,
412 attendance_list: &mut c.attendance_list,
413 healers: &mut c.healers,
414 happiness: &mut c.happiness,
415 calendar: c.calendar,
416 year: c.year,
417 fruits: &mut c.fruits,
418 wildlife: &mut *env.wildlife,
419 keep_piles: &mut c.keep_piles,
420 site_events: &mut c.site_events,
421 storage_changes: &mut c.storage_changes,
422 rebuilds: &mut c.rebuilds,
423 site_dt: 0,
424 water: &mut c.water,
425 game_flags: c.game_flags,
426 precipitation: c.precipitation,
427 population: c.population,
428 visitors: &mut c.visitors,
429 trading_posts: &mut c.trading_posts,
430 trade_value: &mut c.trade_value,
431 known_materials: &mut c.known_materials,
432 known: &mut c.known,
433 pending_roads: &mut c.pending_roads,
434 terraform_requests: &mut c.terraform_requests,
435 flatten_requests: &mut c.flatten_requests,
436 tornadoes: &mut c.tornadoes,
437 tornado_kills: &mut c.tornado_kills,
438 trackers: &mut c.trackers,
439 decals: &mut c.decals,
440 counters: &mut c.counters,
441 trade_goods: &c.trade_goods,
442 river: &c.river,
443 pathfinder: &mut self.pathfinder,
444 names: c.names.as_deref(),
445 cemeteries: &mut c.cemeteries,
446 tombstones: &mut c.tombstones,
447 burials: &mut c.burials,
448 site_list: &mut c.site_list,
449 events: Vec::new(),
450 piles: &mut c.piles,
451 claimed: &mut c.claimed_resources,
452 new_piles: &mut c.new_piles,
453 models_dirty: &mut c.models_dirty,
454 offers: Vec::new(),
455 offers_again: false,
456 removed: Vec::new(),
457 animation_events: Vec::new(),
458 produce_resume: None,
459 pasture_stage: false,
460 },
461 &mut c.jobs,
462 )
463 }
464
465 /// Adults without a school: the citizens professions count.
466 fn adults(&self, citizens: &[Citizen]) -> Vec<bool> {
467 citizens
468 .iter()
469 .zip(&self.states)
470 .map(|(c, s)| c.alive && c.age >= self.rules.thresholds.adult_age && s.school.is_none())
471 .collect()
472 }
473}