Skip to main content

banished_game/behaviour/agents/
views.rs

1//! What the agents see of the town: the map sync, entity views and storages.
2
3use super::*;
4
5/// Entities placed during the update (piles, plantings) join the views; the original reads
6/// its entities live.
7pub(super) fn refresh_views(entities: &mut Vec<EntityView>, env: &Env) {
8    if entities.len() != env.placed.len() {
9        *entities = entity_views(env.placed, env.templates, env.construction);
10    }
11}
12
13/// The parts of each placed entity the activities read.
14pub fn entity_views(
15    placed: &[Placed],
16    templates: &[Template],
17    construction: &crate::work::construction::Construction,
18) -> Vec<EntityView> {
19    // Entities and sites are both in index order, so one pass walks the sites alongside the
20    // entities instead of looking each entity up in the map (28,000 lookups per call in a
21    // large town). The output is the same as looking each up.
22    let mut sites = construction.sites.iter().peekable();
23    placed
24        .iter()
25        .enumerate()
26        .map(|(e, p)| {
27            while sites.next_if(|(&k, _)| k < e).is_some() {}
28            let site = sites.next_if(|(&k, _)| k == e).map(|(_, s)| s);
29            let t = &templates[p.template];
30            let rect = t.map.as_ref().map(|_| {
31                let (w, h) = p.footprint;
32                (p.cell.0, p.cell.1, w, h)
33            });
34            // The entity's use points, then its MultiObject parts' (`FUN_140128540`,
35            // `FUN_1401282f0`), each turned by its own transform.
36            let parts = site.map_or(&[][..], |s| &s.parts[..]);
37            // Most entities (trees, rocks, piles) have no use points and no parts: skip the
38            // iterator chain for them. The result is the same empty list; in a town of 28,000
39            // entities the chain alone was a quarter of the simulation time.
40            let use_points = if parts.is_empty() && t.use_points.is_empty() {
41                Vec::new()
42            } else {
43                std::iter::once(e)
44                    .chain(parts.iter().copied())
45                    .filter_map(|x| placed.get(x))
46                    .flat_map(|q| {
47                        let qt = &templates[q.template];
48                        let model = q.model(qt);
49                        qt.use_points.iter().map(move |&l| model.apply(l))
50                    })
51                    .collect()
52            };
53            // `FUN_14004b080`: registered, and not the unhappy kind.
54            let happiness = site.and_then(|site| {
55                let d = construction.templates[site.template].happiness?;
56                (site.happiness_registered && d.kind != 5).then_some(d.pad)
57            });
58            EntityView {
59                alive: p.alive,
60                rect: if p.alive { rect } else { None },
61                use_points,
62                has_interact: t.has_interact,
63                position: p.position,
64                has_model: t.model.is_some(),
65                happiness,
66            }
67        })
68        .collect()
69}
70
71/// An entity's StorageComponent: a construction site's (houses, workplaces), else a town
72/// storage's.
73pub(super) fn storage_of(env: &Env, entity: usize) -> Option<usize> {
74    env.construction
75        .sites
76        .get(&entity)
77        .map(|s| s.storage)
78        .or_else(|| {
79            env.sites
80                .iter()
81                .find(|s| s.entity == entity)
82                .map(|s| s.storage)
83        })
84}
85
86impl Agents {
87    /// The path sectors' marks in the order the cells flipped (`Cells::set_type`, made on the
88    /// spot by `FUN_1400ae160` in the original: a rebuilt sector hands its node ids out in
89    /// mark order); then `FUN_1400a1950` and the path grid's type commands where the map
90    /// changed.
91    pub(super) fn sync_map(&mut self, cells: &Cells) {
92        for &i in cells
93            .sector_marks
94            .get(self.marks_seen..)
95            .unwrap_or_default()
96        {
97            let i = i as usize;
98            self.sectors
99                .mark((i % cells.size) as i32, (i / cells.size) as i32);
100        }
101        self.marks_seen = cells.sector_marks.len();
102        // Comparing the whole map (262,144 cells on a large map) six times an iteration was
103        // the largest single cost in a large town; the revision says whether anything changed.
104        if self.types_seen == cells.types_revision {
105            return;
106        }
107        self.types_seen = cells.types_revision;
108        if self.region_types == cells.types {
109            return;
110        }
111        // The regions depend only on which cells are walkable (`regions`), so the map-wide
112        // fill runs again only when a change makes a cell walkable or not walkable. In a large
113        // town fields, roads and plantings change cell types often without that.
114        let mut walkability_changed = false;
115        for (i, (old, new)) in self.region_types.iter_mut().zip(&cells.types).enumerate() {
116            if *old != *new {
117                self.pathfinder.set_type(i, *new);
118                walkability_changed |=
119                    crate::pathfind::walkable(*old) != crate::pathfind::walkable(*new);
120                *old = *new;
121            }
122        }
123        if walkability_changed {
124            self.regions = regions(cells.size, &cells.types);
125        }
126    }
127}