banished_game/people/
services.rs1use crate::behaviour::character::{Action, MoveTo};
7use crate::pathfind::WALKABLE;
8use crate::scheduler::ticks;
9use crate::town::Sim;
10use crate::work::construction::Attendance;
11use crate::work::jobs::{Jobs, Work, WorkKind};
12use crate::work::stock::StorageSearchMap;
13
14#[derive(Debug, Clone, Default, PartialEq)]
16pub struct LocationWork {
17 pub worker: Option<usize>,
20}
21
22impl Sim<'_, '_> {
23 pub(crate) fn attendance_enter(&mut self, jobs: &mut Jobs, e: usize) {
25 let site = &self.sites[&e];
26 let capacity = self.site_templates[site.template].attendance.unwrap_or(15);
27 let profession = self.workforce.workplaces.get(&e).and_then(|w| w.profession);
28 self.attendance.entry(e).or_insert(Attendance {
29 capacity,
30 attendees: Vec::new(),
31 profession,
32 });
33 if !self.attendance_list.contains(&e) {
34 self.attendance_list.push(e);
35 }
36 let work = Work::new(
37 e,
38 self.rect_cell(e),
39 profession.unwrap_or(self.professions.laborer),
40 WorkKind::Location(LocationWork::default()),
41 );
42 self.set_work(jobs, e, work, 1, false);
43 if let Some(w) = self.workforce.workplaces.get_mut(&e) {
44 w.enabled = true;
45 }
46 }
47
48 pub(crate) fn attendance_leave(&mut self, e: usize) {
50 self.attendance_list.retain(|&x| x != e);
51 self.evict(e);
52 self.enable_workplace(e, false);
53 }
54
55 pub(crate) fn evict(&mut self, e: usize) {
57 let attendees = self
58 .attendance
59 .get_mut(&e)
60 .map(|a| std::mem::take(&mut a.attendees))
61 .unwrap_or_default();
62 for c in attendees {
63 let s = &mut self.states[c];
64 if s.school == Some(e) {
65 s.school = None;
66 }
67 if s.doctor == Some(e) {
68 s.doctor = None;
69 }
70 if s.priest == Some(e) {
71 s.priest = None;
72 }
73 }
74 }
75
76 pub fn update_attendance(&mut self, visits: &[(usize, i64)]) {
81 let empty: Vec<usize> = visits
82 .iter()
83 .filter_map(|&(e, _)| Some((e, self.attendance.get(&e)?)))
84 .filter(|(e, a)| {
85 !a.attendees.is_empty()
86 && self
87 .workforce
88 .workplaces
89 .get(e)
90 .is_none_or(|w| w.workers.is_empty())
91 })
92 .map(|(e, _)| e)
93 .collect();
94 for e in empty {
95 self.evict(e);
96 }
97 }
98
99 pub(crate) fn attendance_full(&self, e: usize) -> bool {
102 let profession = |x: &usize| self.workforce.workplaces.get(x).and_then(|w| w.profession);
103 !self.attendance_list.iter().any(|x| {
104 profession(x) == profession(&e)
105 && self
106 .attendance
107 .get(x)
108 .is_some_and(|a| (a.attendees.len() as i32) < a.capacity)
109 })
110 }
111
112 pub(crate) fn location_try(&mut self, work: &mut Work, c: usize) -> u8 {
114 let WorkKind::Location(l) = &work.kind else {
115 return 0;
116 };
117 if l.worker.is_some() {
118 return 1;
119 }
120 if self.states[c].carried.used(self.materials) > 0 {
121 self.drop_off(c);
122 }
123 let from = self.citizen_cell(c);
125 let Some(target) = self.nearest_use_point(work.entity, from) else {
126 return 0;
127 };
128 self.clear_citizen(c, Some(work));
130 self.on_empty_think(c);
131 self.push(
132 c,
133 Action::CompleteTask {
134 work: work.id,
135 param: 0,
136 },
137 );
138 self.push(
139 c,
140 Action::PerformWork {
141 facing: -1000.0,
142 anim: 8,
143 duration: ticks(1.0),
144 elapsed: 0,
145 consume: false,
146 },
147 );
148 let walk = MoveTo::new(target, 1, 0, WALKABLE, 0, -1.0, self.rng);
149 self.push(c, Action::move_to(walk));
150 let s = &mut self.states[c];
151 s.health_events |= 4;
152 s.job = work.id;
153 s.supply_activity = 0;
154 if work.profession == s.profession {
155 s.flags |= 0x20000;
156 }
157 if let WorkKind::Location(l) = &mut work.kind {
158 l.worker = Some(c);
159 }
160 2
161 }
162
163 pub(crate) fn nearest_use_point(&mut self, e: usize, from: i32) -> Option<[f32; 3]> {
166 let points = self.entities.get(e)?.use_points.clone();
167 let mut best = 0xffff;
168 let mut found = None;
169 for p in points {
170 let to = self.cell_of(p);
171 let town = crate::behaviour::activities::TownView {
172 size: self.size,
173 types: &self.cells.types,
174 regions: self.regions,
175 occupants: &self.cells.occupants,
176 objects: &self.cells.objects,
177 floors: &self.cells.floors,
178 entities: self.entities,
179 happiness: self.happiness,
180 };
181 if !town.reachable(WALKABLE, from, to) {
182 continue;
183 }
184 let rank = self.geometry_rank(from, to);
185 if rank < best {
186 best = rank;
187 found = Some(p);
188 }
189 }
190 found
191 }
192
193 pub(crate) fn geometry_rank(&mut self, a: i32, b: i32) -> i32 {
195 let (size, terrain) = (self.size, self.terrain);
196 let floors = &self.cells.floors;
197 let ground = move |c: i32| crate::town::floor_ground(floors, size, terrain, c);
198 let town = crate::behaviour::activities::TownView {
199 size: self.size,
200 types: &self.cells.types,
201 regions: self.regions,
202 occupants: &self.cells.occupants,
203 objects: &self.cells.objects,
204 floors: &self.cells.floors,
205 entities: self.entities,
206 happiness: self.happiness,
207 };
208 let mut g = crate::behaviour::activities::StorageGeometry {
209 town: &town,
210 sectors: self.sectors,
211 ground: &ground,
212 };
213 g.rank(a, b)
214 }
215}