Skip to main content

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}