Skip to main content

banished_game/behaviour/agents/
update.rs

1//! The agents' iteration: characters, animals and hunt messages.
2
3use super::*;
4
5impl Agents {
6    /// One simulation iteration of every citizen. `groups[i]` is citizen `i`'s animation group
7    /// (`None` for the dead); `newborn` makes a child, with its random draws.
8    pub fn update(
9        &mut self,
10        citizens: &mut Vec<Citizen>,
11        env: &mut Env,
12        groups: &[Option<&AnimationGroup>],
13        dt: f32,
14        newborn: &mut dyn FnMut(&mut Random, &Citizen, bool) -> Citizen,
15    ) {
16        self.sync_map(env.cells);
17        // This iteration's bucket visits: placed entities, and the citizens (from `EXTRA`).
18        let (extra, foreign) = (crate::scheduler::EXTRA, crate::scheduler::FOREIGN);
19        let site_visits: Vec<(usize, i64)> = env
20            .visits
21            .iter()
22            .copied()
23            .filter(|&(v, _)| v < extra)
24            .collect();
25        let citizen_visits: Vec<(usize, i64)> = env
26            .visits
27            .iter()
28            .filter(|&&(v, _)| (extra..foreign).contains(&v))
29            .map(|&(v, e)| (v - extra, e))
30            .collect();
31        // The town step (`FUN_140091780`): the population, and the visitors' disease risk
32        // running out.
33        {
34            let c = &mut *env.construction;
35            c.population = self.registry.len() as i32;
36            c.visitors.1 -= ticks(dt);
37            if c.visitors.1 < 0 {
38                c.visitors = (0.0, 0);
39            }
40            self.town_disease = c.visitors.0;
41        }
42        // `FUN_140118340`'s job icon (bit 3): no workplace for a profession that needs one.
43        let professions = env.construction.professions.clone();
44        for state in &mut self.states {
45            let anywhere = if professions.list.is_empty() {
46                self.rules.profession_allows_idle
47            } else {
48                professions.works_anywhere(state.profession)
49            };
50            crate::people::status::set_icon(
51                &mut state.status,
52                3,
53                !anywhere && state.workplace.is_none(),
54                false,
55            );
56        }
57        self.sectors.age_cache(ticks(dt));
58        let mut entities = entity_views(env.placed, env.templates, env.construction);
59        let mut animating = crate::town::animating(env.placed, env.templates);
60        let adult = self.adults(citizens);
61        for (ch, group) in self.characters.iter_mut().zip(groups) {
62            if let Some(g) = group {
63                ch.animate(g, dt);
64            }
65        }
66        // The citizens' CharacterComponents in the characters' tree: the dead leave it
67        // (`FUN_14010ee50`), the new join it (`FUN_14010ee90`), in index order.
68        // Births, bands and deaths join and leave it on the spot (`join_tree`, `remove`); this
69        // catches the start's citizens and any made elsewhere.
70        for i in 0..self.characters.len() {
71            let alive = groups.get(i).copied().flatten().is_some()
72                && citizens.get(i).is_some_and(|c| c.alive);
73            let ch = &mut self.characters[i];
74            if !alive && 0 <= ch.tree_slot {
75                env.wildlife.tree.remove(ch.tree_slot);
76                ch.tree_slot = -1;
77            }
78        }
79        for i in 0..self.characters.len() {
80            let alive = groups.get(i).copied().flatten().is_some()
81                && citizens.get(i).is_some_and(|c| c.alive);
82            let ch = &mut self.characters[i];
83            if alive && ch.tree_slot < 0 {
84                ch.tree_slot = env.wildlife.tree.insert(
85                    crate::behaviour::character_tree::Who::Citizen(i),
86                    ch.position,
87                    ch.desc.radius,
88                );
89            }
90        }
91        for i in 0..self.characters.len() {
92            let Some(group) = groups[i] else { continue };
93            let mut messages = Vec::new();
94            let mut used = Vec::new();
95            {
96                let state = &mut self.states[i];
97                // The current job's work (`+0x30`) and its profession's accident odds.
98                let accident_odds = (0 <= state.job)
99                    .then(|| env.construction.jobs.works.get(&state.job))
100                    .flatten()
101                    .and_then(|w| {
102                        let p = env.construction.professions.list.get(w.profession)?;
103                        Some((w.profession, p.accident.0, p.accident.1))
104                    });
105                // `Town::FindMaterialStorage` for a redirected drop-off.
106                let town = crate::behaviour::activities::TownView {
107                    size: env.cells.size,
108                    types: &env.cells.types,
109                    regions: &self.regions,
110                    occupants: &env.cells.occupants,
111                    objects: &env.cells.objects,
112                    floors: &env.cells.floors,
113                    entities: &entities,
114                    happiness: &env.construction.happiness,
115                };
116                let (size, terrain, sites, materials) =
117                    (env.cells.size, env.terrain, env.sites, env.materials);
118                let floors = &env.cells.floors;
119                let sectors = &mut self.sectors;
120                let mut search = |storages: &[Storage],
121                                  rng: &mut Random,
122                                  m: usize,
123                                  n: i32,
124                                  cell: i32|
125                 -> Option<(usize, [f32; 3])> {
126                    let ground =
127                        move |cell: i32| crate::town::floor_ground(floors, size, terrain, cell);
128                    let mut geometry = crate::behaviour::activities::StorageGeometry {
129                        town: &town,
130                        sectors: &mut *sectors,
131                        ground: &ground,
132                    };
133                    crate::work::stock::find_material_storage(
134                        sites,
135                        storages,
136                        materials,
137                        m,
138                        n,
139                        true,
140                        cell,
141                        None,
142                        &mut geometry,
143                        rng,
144                    )
145                    .map(|f| (f.storage, f.target))
146                };
147                let mut world = World {
148                    size: env.cells.size,
149                    types: &env.cells.types,
150                    objects: &env.cells.objects,
151                    occupants: &env.cells.occupants,
152                    flags: &env.cells.flags,
153                    roads: &env.cells.road_kind,
154                    floors: &env.cells.floors,
155                    regions: &self.regions,
156                    terrain: env.terrain,
157                    pathfinder: &mut self.pathfinder,
158                    rng: env.rng,
159                    messages: &mut messages,
160                    used: &mut used,
161                    materials: env.materials,
162                    storages: env.storages,
163                    storage_search: Some(&mut search),
164                    pushes: Vec::new(),
165                    carried: &mut state.carried,
166                    tool_uses: &mut state.tool_uses,
167                    shown_tool: &mut state.shown_tool,
168                    animating: &mut animating,
169                    accident: &mut state.accident,
170                    accident_timer: &mut state.accident_timer,
171                    accident_odds,
172                };
173                self.characters[i].update(&mut world, group, dt);
174                // `FUN_14010e990` ends by placing it in the tree again (`FUN_140019070`).
175                let ch = &self.characters[i];
176                env.wildlife
177                    .tree
178                    .place(ch.tree_slot, ch.position, ch.desc.radius);
179                env.wildlife.tree.set_direction(ch.tree_slot, ch.direction);
180            }
181            for (m, n) in used {
182                if let Some(slot) = crate::work::produce::production_group(env.materials[m].flags) {
183                    env.construction.counters.used[slot] += n;
184                }
185            }
186            citizens[i].position = self.characters[i].position;
187            self.deliver(i, messages, citizens, env, groups, &adult, &mut entities);
188        }
189        // The sites' StateMachine updates (bit 4, then their bucket); the piles' ages
190        // (RawMaterialComponent's scheduled update, run every iteration here).
191        {
192            let (mut sim, jobs) = self.sim(citizens, env, groups, &adult, &entities);
193            sim.update_sites(jobs, ticks(dt), &site_visits);
194            sim.update_nomads(ticks(dt));
195            sim.update_boats();
196            sim.update_attendance(&site_visits);
197            sim.age_piles(jobs, &site_visits);
198            sim.update_hunts(&site_visits);
199            sim.update_blight(&site_visits);
200            sim.update_fires(jobs, &site_visits);
201            sim.update_tornadoes(jobs, ticks(dt));
202            sim.place_burials();
203            jobs.drain(&mut sim);
204        }
205        // The tornadoes' victims (`FUN_14011c2a0(citizen, 5)` from `FUN_140058d20`).
206        for c in std::mem::take(&mut env.construction.tornado_kills) {
207            if !citizens[c].alive {
208                continue;
209            }
210            let mut born = Vec::new();
211            self.with_people(citizens, env, &entities, newborn, &mut born, |people| {
212                people.die(c, crate::people::life::reason::TORNADO, None);
213            });
214        }
215        // The residences' state-2 updates (`FUN_14012ee40`): the ClearStorage work, then the
216        // fire.
217        {
218            let (mut sim, jobs) = self.sim(citizens, env, groups, &adult, &entities);
219            sim.residence_clear_storage(jobs, &site_visits);
220            jobs.drain(&mut sim);
221        }
222        {
223            let c = &mut *env.construction;
224            for &(e, elapsed) in &site_visits {
225                let Some(home) = c.homes.get_mut(&e) else {
226                    continue;
227                };
228                crate::people::life::update_home(
229                    home,
230                    env.storages,
231                    env.materials,
232                    env.temperature,
233                    elapsed,
234                    &mut c.counters,
235                );
236            }
237        }
238        // The citizens bucket 1 visited this iteration, in its order, each with the time since
239        // its last visit (`FUN_1400c9560`).
240        for &(i, elapsed) in &citizen_visits {
241            if i >= self.ais.len()
242                || groups.get(i).copied().flatten().is_none()
243                || !citizens[i].alive
244            {
245                continue;
246            }
247            // CharacterComponent's scheduled update (`FUN_14010e950`): the running chain's
248            // notify, a hunter's `HuntAnimal`.
249            refresh_views(&mut entities, env);
250            self.hunt_notify(i, env, groups);
251            let following = env.wildlife.tree.follows(self.characters[i].tree_slot);
252            self.characters[i].stack.notify_moves(following);
253            // CitizenComponent before CitizenAIComponent (the template's component order); a
254            // citizen queued for deletion stops there (entity flag `0x400`).
255            let born = self.live(i, citizens, env, &entities, elapsed, newborn);
256            for c in born {
257                let mut ch = Character::new(self.desc, citizens[c].position, CHILD_SCALE);
258                ch.yaw = self.characters[i].yaw;
259                self.characters.push(ch);
260                self.ais.push(CitizenAi::default());
261                self.states[c].carried = self.carried.clone();
262                // The child's entity activates as it is made (`FUN_14010ee90`).
263                self.join_tree(c, env);
264            }
265            if self.dying.iter().any(|&(d, _)| d == i) {
266                continue;
267            }
268            refresh_views(&mut entities, env);
269            self.think(i, citizens, env, groups, &adult, &entities, elapsed);
270        }
271        // The entity manager deletes the queued entities (each component's remove slot);
272        // removals can queue more (orphaned children).
273        let mut k = 0;
274        while k < self.dying.len() {
275            let (c, _) = self.dying[k];
276            self.remove(c, citizens, env, groups, &entities);
277            k += 1;
278        }
279        self.dying.clear();
280        for (c, s) in citizens.iter_mut().zip(&self.states) {
281            c.school = s.school;
282        }
283    }
284
285    /// The wild animals' character update: animate, run the actions, turn and move.
286    ///
287    /// Deviation: the entity manager runs these updates in the order of a tree keyed by the
288    /// entity's address; the port takes them in the order they were made, which a growing heap
289    /// usually gives too.
290    ///
291    /// Original: `FUN_14010e990`; the order comes from `FUN_1400c9560`, `FUN_1400c82b0` and the
292    /// tree at manager `+0x68` (`FUN_1400c90a0`).
293    pub fn move_animals(
294        &mut self,
295        wildlife: &mut crate::nature::wildlife::Wildlife,
296        cells: &Cells,
297        terrain: &HeightGrid,
298        rng: &mut Random,
299        materials: &[RawMaterial],
300        dt: f32,
301    ) {
302        self.sync_map(cells);
303        let mut animating = Vec::new();
304        let mut storages: Vec<Storage> = Vec::new();
305        let ids: Vec<usize> = wildlife.animals.keys().copied().collect();
306        for a in ids {
307            let (template, variant) = {
308                let animal = &wildlife.animals[&a];
309                if animal.dying {
310                    continue;
311                }
312                (animal.template, animal.variant)
313            };
314            let Some(group) = wildlife.groups.get(&(template, variant)).cloned() else {
315                continue;
316            };
317            let animal = wildlife.animals.get_mut(&a).unwrap();
318            let mut messages = Vec::new();
319            let (mut carried, mut tool_uses, mut shown_tool) = (Storage::default(), 0, -1);
320            let (mut accident, mut accident_timer) = (None, 0);
321            let mut world = World {
322                size: cells.size,
323                types: &cells.types,
324                objects: &cells.objects,
325                occupants: &cells.occupants,
326                flags: &cells.flags,
327                roads: &cells.road_kind,
328                floors: &cells.floors,
329                regions: &self.regions,
330                terrain,
331                pathfinder: &mut self.pathfinder,
332                rng,
333                messages: &mut messages,
334                used: &mut Vec::new(),
335                materials,
336                storages: &mut storages,
337                carried: &mut carried,
338                tool_uses: &mut tool_uses,
339                storage_search: None,
340                pushes: Vec::new(),
341                shown_tool: &mut shown_tool,
342                animating: &mut animating,
343                accident: &mut accident,
344                accident_timer: &mut accident_timer,
345                accident_odds: None,
346            };
347            animal.character.animate(&group, dt);
348            animal.character.update(&mut world, &group, dt);
349            let ch = &animal.character;
350            wildlife
351                .tree
352                .place(ch.tree_slot, ch.position, ch.desc.radius);
353            wildlife.tree.set_direction(ch.tree_slot, ch.direction);
354        }
355        // The trader boats' CharacterComponent.
356        let ids: Vec<usize> = wildlife.boats.keys().copied().collect();
357        for id in ids {
358            let b = &wildlife.boats[&id];
359            let Some(group) = wildlife.groups.get(&(b.template, 0)).cloned() else {
360                continue;
361            };
362            let boat = wildlife.boats.get_mut(&id).unwrap();
363            let mut messages = Vec::new();
364            let (mut carried, mut tool_uses, mut shown_tool) = (Storage::default(), 0, -1);
365            let (mut accident, mut accident_timer) = (None, 0);
366            let mut world = World {
367                size: cells.size,
368                types: &cells.types,
369                objects: &cells.objects,
370                occupants: &cells.occupants,
371                flags: &cells.flags,
372                roads: &cells.road_kind,
373                floors: &cells.floors,
374                regions: &self.regions,
375                terrain,
376                pathfinder: &mut self.pathfinder,
377                rng,
378                messages: &mut messages,
379                used: &mut Vec::new(),
380                materials,
381                storages: &mut storages,
382                carried: &mut carried,
383                tool_uses: &mut tool_uses,
384                storage_search: None,
385                pushes: Vec::new(),
386                shown_tool: &mut shown_tool,
387                animating: &mut animating,
388                accident: &mut accident,
389                accident_timer: &mut accident_timer,
390                accident_odds: None,
391            };
392            boat.character.animate(&group, dt);
393            boat.character.update(&mut world, &group, dt);
394            let ch = &boat.character;
395            wildlife
396                .tree
397                .place(ch.tree_slot, ch.position, ch.desc.radius);
398            wildlife.tree.set_direction(ch.tree_slot, ch.direction);
399        }
400        // The nomads' CharacterComponent.
401        let ids: Vec<usize> = wildlife.nomads.keys().copied().collect();
402        for id in ids {
403            let n = &wildlife.nomads[&id];
404            let Some(group) = wildlife.groups.get(&(n.template, n.variant)).cloned() else {
405                continue;
406            };
407            let nomad = wildlife.nomads.get_mut(&id).unwrap();
408            let mut messages = Vec::new();
409            let (mut carried, mut tool_uses, mut shown_tool) = (Storage::default(), 0, -1);
410            let (mut accident, mut accident_timer) = (None, 0);
411            let mut world = World {
412                size: cells.size,
413                types: &cells.types,
414                objects: &cells.objects,
415                occupants: &cells.occupants,
416                flags: &cells.flags,
417                roads: &cells.road_kind,
418                floors: &cells.floors,
419                regions: &self.regions,
420                terrain,
421                pathfinder: &mut self.pathfinder,
422                rng,
423                messages: &mut messages,
424                used: &mut Vec::new(),
425                materials,
426                storages: &mut storages,
427                carried: &mut carried,
428                tool_uses: &mut tool_uses,
429                storage_search: None,
430                pushes: Vec::new(),
431                shown_tool: &mut shown_tool,
432                animating: &mut animating,
433                accident: &mut accident,
434                accident_timer: &mut accident_timer,
435                accident_odds: None,
436            };
437            nomad.character.animate(&group, dt);
438            nomad.character.update(&mut world, &group, dt);
439            let ch = &nomad.character;
440            wildlife
441                .tree
442                .place(ch.tree_slot, ch.position, ch.desc.radius);
443            wildlife.tree.set_direction(ch.tree_slot, ch.direction);
444        }
445    }
446
447    /// `HuntAnimal`'s notify (`FUN_1400cd2b0`): once the lodge has gathered its hunting time
448    /// (`FUN_140127f50`), the nearest wild animal within 8 (`FUN_140097730`), not already
449    /// taken, next to a cell the hunter can reach, is taken (`FUN_1400c1370` without clearing:
450    /// walk there, shoot 4 s, remove it, leave its raw materials where it stood), and its herd
451    /// moves on (`FUN_140127810`).
452    pub(super) fn hunt_notify(
453        &mut self,
454        i: usize,
455        env: &mut Env,
456        groups: &[Option<&AnimationGroup>],
457    ) {
458        let Some(group) = groups.get(i).copied().flatten() else {
459            return;
460        };
461        let Some((lodge, done)) = self.characters[i].stack.hunt_watch() else {
462            return;
463        };
464        if *done {
465            return;
466        }
467        let Some(site) = env.construction.sites.get(&lodge) else {
468            return;
469        };
470        let Some(d) = env.construction.templates[site.template].hunt else {
471            return;
472        };
473        if site.hunt_time.unwrap_or(0) < ticks(d.time * 300.0) {
474            return;
475        }
476        let pos = self.characters[i].position;
477        // `FUN_140097730`: the nearest character with a WildAnimalComponent
478        // (`DAT_1401d0000`; livestock have none) within 8 of the hunter, the first of equals
479        // in the characters' tree's order, which finds nothing while the hunter stands more
480        // than 9 from height 0. Whether it is hunted already is asked after.
481        let animals = &env.wildlife.animals;
482        let best = env
483            .wildlife
484            .tree
485            .nearest(pos, 8.0, |who| match who {
486                crate::behaviour::character_tree::Who::Animal(a) => {
487                    animals.get(&a).is_some_and(|x| x.livestock.is_none())
488                }
489                _ => false,
490            })
491            .and_then(|who| match who {
492                crate::behaviour::character_tree::Who::Animal(a) => Some(a),
493                _ => None,
494            });
495        let Some(a) = best else { return };
496        let animal = &env.wildlife.animals[&a];
497        if animal.flags & crate::nature::wildlife::HUNTED != 0 {
498            return;
499        }
500        let n = env.cells.size as i32;
501        let cell_of = |p: [f32; 3]| {
502            let half = (n >> 1) as f32;
503            let x = ((p[0] + half) as i32).clamp(0, n - 1);
504            let y = ((p[1] + half) as i32).clamp(0, n - 1);
505            n * y + x
506        };
507        let (here, at) = (cell_of(pos), cell_of(animal.character.position));
508        let Some(nb) = crate::pathfind::nearest_neighbour(
509            env.cells.size,
510            &env.cells.types,
511            at,
512            here,
513            WALKABLE,
514        ) else {
515            return;
516        };
517        let town = TownView {
518            size: env.cells.size,
519            types: &env.cells.types,
520            regions: &self.regions,
521            occupants: &env.cells.occupants,
522            objects: &env.cells.objects,
523            floors: &env.cells.floors,
524            entities: &[],
525            happiness: &[],
526        };
527        if !town.reachable(WALKABLE, nb, here) {
528            return;
529        }
530        if let Some((_, done)) = self.characters[i].stack.hunt_watch() {
531            *done = true;
532        }
533        let materials = env
534            .wildlife
535            .animal_kinds
536            .get(&animal.template)
537            .map(|k| k.materials.clone())
538            .unwrap_or_default();
539        let herd = animal.herd;
540        let batch = self.states[i].carried.capacity;
541        let workplace = self.states[i].workplace;
542        let mut duration = ticks(4.0);
543        if self.states[i].tool_uses < 1 {
544            duration = ticks(crate::scheduler::seconds(duration) * self.rules.no_tool_factor);
545        }
546        let target = crate::town::floor_ground(&env.cells.floors, env.cells.size, env.terrain, nb);
547        let walk = MoveTo::new(target, 1, 0, WALKABLE, 0, -1.0, env.rng);
548        let ch = &mut self.characters[i];
549        let mut stack = std::mem::take(&mut ch.stack);
550        for &m in &materials {
551            stack.push(
552                ch,
553                group,
554                Action::CreateRawMaterial {
555                    template: m,
556                    cell: at,
557                    batch,
558                    workplace,
559                    factor: 1.0,
560                },
561            );
562        }
563        stack.push(ch, group, Action::RemoveAnimal { entity: a });
564        stack.push(
565            ch,
566            group,
567            Action::PerformWork {
568                facing: -1000.0,
569                anim: 0xb,
570                duration,
571                elapsed: 0,
572                consume: true,
573            },
574        );
575        stack.push(ch, group, Action::move_to(walk));
576        ch.stack = stack;
577        // `FUN_14010c730`, then the herd's new target.
578        let mut scheduler = crate::scheduler::EntityScheduler::default();
579        let mut wild = crate::nature::wildlife::Wild {
580            wildlife: env.wildlife,
581            placed: env.placed,
582            templates: env.templates,
583            cells: env.cells,
584            terrain: env.terrain,
585            rng: env.rng,
586            // Taking an animal creates no entity.
587            scheduler: &mut scheduler,
588        };
589        wild.hunted(a);
590        if let Some(h) = herd {
591            wild.retarget(h);
592        }
593        if let Some(t) = env
594            .construction
595            .sites
596            .get_mut(&lodge)
597            .and_then(|s| s.hunt_time.as_mut())
598        {
599            *t = 0;
600        }
601    }
602}