1use super::*;
4
5impl Sim<'_, '_> {
6 pub(super) fn entity_on(&self, cell: i32) -> Option<usize> {
8 self.cells.entity_at(cell as usize)
9 }
10
11 pub(super) fn register_clear(&mut self, jobs: &mut Jobs, e: usize) {
15 let site = &self.sites[&e];
16 let Some(clear) = self.site_templates[site.template].clear.clone() else {
17 return;
18 };
19 let n = self.size as i32;
20 let (x, y) = self.placed[e].cell;
21 let (w, h) = self.placed[e].footprint;
22 let mut mask = 1u32;
23 for cy in y..y + h {
24 for cx in x..x + w {
25 let Some(o) = self.cells.objects[(cy * n + cx) as usize] else {
26 continue;
27 };
28 let t = &self.templates[self.placed[o].template];
29 if t.natural.is_none() {
30 continue;
31 }
32 if let Some(m) = t.pile.and_then(|p| self.templates[p].pile_material) {
33 mask |= clear_mask(self.materials[m].flags);
34 }
35 }
36 }
37 let total = self.clear_remaining(e);
38 if let Some(site) = self.sites.get_mut(&e) {
39 site.clear_total = total;
40 }
41 let work = Work::new(
42 e,
43 self.rect_cell(e),
44 self.professions.laborer,
45 WorkKind::Clear(ClearWork {
46 filter: clear.reference.clone(),
47 clear_resources: clear.clear_resources,
48 clear_cells: clear.clear_cells,
49 clear_piles: clear.clear_piles,
50 ..Default::default()
51 }),
52 );
53 self.set_work(jobs, e, work, mask, clear.keep_priority);
54 }
55
56 pub(crate) fn work_max(&self, e: usize) -> i32 {
59 match self.workforce.workplaces.get(&e) {
60 Some(w) => w.max(),
61 None => self.sites.get(&e).map_or(0, |s| {
62 self.site_templates[s.template].default_max_workers(self.placed[e].footprint)
63 }),
64 }
65 }
66
67 pub fn clear_remaining(&self, e: usize) -> i32 {
71 clear_remaining(&self.cells.objects, self.size as i32, &self.placed[e], e)
72 }
73
74 pub(super) fn clear_refresh(&mut self, work: &mut Work) {
77 let id = work.id;
78 let WorkKind::Clear(cl) = &mut work.kind else {
79 return;
80 };
81 let lost: Vec<usize> = cl
82 .resources
83 .iter()
84 .filter(|(_, &c)| self.characters[c].stack.holds_task(id).is_none())
85 .map(|(&r, _)| r)
86 .collect();
87 if cl
88 .cells
89 .values()
90 .any(|&c| self.characters[c].stack.holds_task(id).is_none())
91 {
92 return;
93 }
94 for r in lost {
95 cl.resources.remove(&r);
96 }
97 }
98
99 pub(super) fn clear_try(&mut self, work: &mut Work, c: usize, time: i64) -> u8 {
101 let e = work.entity;
102 let max = self.work_max(e);
103 let WorkKind::Clear(cl) = &work.kind else {
104 return 0;
105 };
106 if max <= (cl.resources.len() + cl.cells.len()) as i32 {
107 return 1;
108 }
109 if self
110 .workforce
111 .workplaces
112 .get(&e)
113 .is_some_and(|w| !w.enabled)
114 {
115 return 1;
116 }
117 let n = self.size as i32;
120 let (x, y) = self.placed[e].cell;
121 let (w, h) = self.placed[e].footprint;
122 let from = self.citizen_cell(c);
123 let clear_cells = cl.clear_cells;
124 let mut entries: Vec<(i32, i32, Option<usize>)> = Vec::new();
125 for cy in y..y + h {
126 for cx in x..x + w {
127 let cell = cy * n + cx;
128 let i = cell as usize;
129 let (dy, dx) = (cell / n - from / n, cell % n - from % n);
130 if let Some(o) = self.cells.objects[i] {
131 entries.push((cell, dy * dy + dx * dx, Some(o)));
132 }
133 if clear_cells {
134 let t = self.cells.types[i].0;
135 let road = t == 0
136 || t == 1
137 || self.cells.road_kind[i] != crate::work::road::NONE
138 || self.pending_roads.contains_key(&cell);
139 if road {
140 entries.push((cell, dy * dy + dx * dx, None));
141 }
142 }
143 }
144 }
145 crate::behaviour::ai::crt_qsort_by(&mut entries, |a, b| a.1 - b.1);
146 let (resources, filter, clear_piles) =
147 (cl.clear_resources, cl.filter.clone(), cl.clear_piles);
148 if resources {
149 for &(cell, _, o) in &entries {
150 let Some(o) = o else { continue };
151 let WorkKind::Clear(cl) = &work.kind else {
152 return 0;
153 };
154 if cl.resources.contains_key(&o) {
155 continue;
156 }
157 let t = &self.templates[self.placed[o].template];
158 if let Some(f) = &filter {
159 if !t.reference.eq_ignore_ascii_case(&f.replace('/', "\\")) {
160 continue;
161 }
162 }
163 if t.natural.is_none() {
164 continue;
165 }
166 let Some(nb) = nearest_neighbour(self.size, &self.cells.types, cell, from, 0xe07)
167 else {
168 continue;
169 };
170 if !self.reachable(nb, from) {
171 continue;
172 }
173 if !self.patience(work, c, nb, from, time) {
174 return 0;
175 }
176 if self.claimed.contains(&o) {
177 continue;
178 }
179 let point = self.ground_at(nb);
180 self.removal_sequence(c, work, o, point, false);
181 self.took_job(c, work);
182 if let WorkKind::Clear(cl) = &mut work.kind {
183 cl.resources.insert(o, c);
184 }
185 return 2;
186 }
187 }
188 if clear_cells {
191 for &(cell, _, o) in &entries {
192 if o.is_some() {
193 continue;
194 }
195 let WorkKind::Clear(cl) = &work.kind else {
196 return 0;
197 };
198 if cl.cells.contains_key(&cell) || !self.reachable(cell, from) {
199 continue;
200 }
201 let target = self.ground_at(cell);
202 self.road_clear_sequence(c, work, target, cell);
203 self.took_job(c, work);
204 if let WorkKind::Clear(cl) = &mut work.kind {
205 cl.cells.insert(cell, c);
206 }
207 return 2;
208 }
209 }
210 if clear_piles {
211 self.offers = entries
213 .iter()
214 .filter_map(|&(_, _, o)| o.and_then(|o| self.piles.get(&o)).map(|p| p.work))
215 .filter(|&id| id >= 0)
216 .collect();
217 if !self.offers.is_empty() {
218 return NESTED;
219 }
220 }
221 1
222 }
223
224 pub(crate) fn removal_sequence(
227 &mut self,
228 c: usize,
229 work: &mut Work,
230 resource: usize,
231 point: [f32; 3],
232 auto_pickup: bool,
233 ) {
234 let t = &self.templates[self.placed[resource].template];
235 let Some(nr) = t.natural.clone() else {
236 return;
237 };
238 let facing = crate::behaviour::character::work_facing(self.facing_centre(resource), point);
239 self.clear_citizen(c, Some(work));
240 self.on_empty_think(c);
241 let tool = nr.value as i32;
242 let clip = if (nr.value) < 11 { tool + 9 } else { 8 } as usize;
243 self.push(
244 c,
245 Action::CompleteTask {
246 work: work.id,
247 param: 0,
248 },
249 );
250 self.push(c, Action::ShowTool(-1));
251 self.push(
252 c,
253 Action::RemoveResource {
254 entity: resource,
255 flag: auto_pickup,
256 },
257 );
258 if nr.flag {
259 self.push(c, self.perform(c, facing, clip, seconds(nr.time_b), false));
260 self.push(
261 c,
262 Action::AnimateResource {
263 entity: resource,
264 anim: 1,
265 wait: false,
266 },
267 );
268 self.push(
269 c,
270 Action::AnimateResource {
271 entity: resource,
272 anim: 0,
273 wait: true,
274 },
275 );
276 }
277 self.push(c, self.perform(c, facing, clip, seconds(nr.time_a), true));
278 let walk = match tool {
279 0 | 1 | 4..=7 => 2,
280 2 | 3 => 3,
281 _ => 1,
282 };
283 let m = MoveTo::new(point, walk, 0, WALKABLE, 0, -1.0, self.rng);
284 self.push(c, Action::move_to(m));
285 self.push(c, Action::ShowTool(tool));
286 self.claimed.insert(resource);
287 }
288
289 pub fn unclaim(&mut self, resource: usize) {
293 self.claimed.remove(&resource);
294 }
295
296 pub fn animate_resource(&mut self, resource: usize, anim: usize) {
300 let p = &mut self.placed[resource];
301 let t = &self.templates[p.template];
302 if t.natural.as_ref().is_some_and(|n| n.flag)
303 && t.animations
304 .get(p.variant)
305 .and_then(Option::as_ref)
306 .is_some_and(|g| g.clips.get(anim).is_some())
307 {
308 p.animation = Some(crate::behaviour::animation::Playback::new(anim, 0));
309 }
310 }
311
312 pub fn remove_resource(
319 &mut self,
320 c: usize,
321 resource: usize,
322 educated: bool,
323 priority: Option<i32>,
324 auto_pickup: bool,
325 ) {
326 self.claimed.remove(&resource);
327 if !self.placed[resource].alive {
328 return;
329 }
330 let template = self.placed[resource].template;
331 let Some(nr) = self.templates[template].natural.clone() else {
332 return;
333 };
334 let mut keep = self.keep_piles.remove(&resource).unwrap_or(true);
337 if let Some(r) = nr.related.as_deref() {
338 let r = r.replace('/', "\\");
339 let related = self
340 .professions
341 .list
342 .iter()
343 .position(|p| p.reference.eq_ignore_ascii_case(&r));
344 if let Some(p) = related {
345 if p != self.professions.laborer && self.states[c].profession != p {
346 keep = false;
347 }
348 }
349 }
350 let growth = self.placed[resource].growth;
353 let cell = self.placed[resource].cell;
354 {
355 let p = &mut self.placed[resource];
356 p.animation = None;
357 p.removal_requested = true;
358 }
359 *self.models_dirty = true;
360 if !keep {
361 return;
362 }
363 let Some(pile_t) = self.templates[template].pile else {
364 return;
365 };
366 let pt = &self.templates[pile_t];
367 let base = if educated {
368 pt.pile_yield.1
369 } else {
370 pt.pile_yield.0
371 };
372 let amount = (base as f32 * growth.min(1.0)) as i32;
373 if pt.pile_material.is_none() || amount <= 0 {
374 return;
375 }
376 let yaw = self.rng.rand_tol(2) as f32 * 90.0;
377 let pile = self.spawn_pile(pile_t, cell, amount, yaw, priority);
378 if let Some(m) = self.templates[pile_t].pile_material {
381 let workplace = self.states[c].workplace;
382 self.count_made(workplace, auto_pickup, m, amount);
383 }
384 if auto_pickup {
386 if let (Some(wp), Some(p)) = (self.states[c].workplace, self.piles.get_mut(&pile)) {
387 p.auto_pickup = Some(wp);
388 }
389 }
390 }
391
392 pub(super) fn clear_instantly(&mut self, jobs: &mut Jobs, e: usize) {
396 let n = self.size as i32;
397 let (x, y) = self.placed[e].cell;
398 let (w, h) = self.placed[e].footprint;
399 for cy in y..y + h {
400 for cx in x..x + w {
401 let Some(o) = self.cells.objects[(cy * n + cx) as usize] else {
402 continue;
403 };
404 if o == e || !self.placed[o].alive || self.placed[o].removal_requested {
405 continue;
406 }
407 if self.piles.contains_key(&o) {
408 self.destroy_pile(jobs, o);
409 continue;
410 }
411 if self.templates[self.placed[o].template].natural.is_some() {
412 let (ox, oy) = self.placed[o].cell;
415 let i = (oy * n + ox) as usize;
416 if self.cells.objects[i] == Some(o) {
417 self.cells.objects[i] = None;
418 self.cells
419 .set_type(i, self.terrain.natural_cell_type(self.size, i));
420 }
421 } else {
422 self.deactivate(jobs, o);
423 }
424 let p = &mut self.placed[o];
425 p.animation = None;
426 p.removal_requested = true;
427 *self.models_dirty = true;
428 }
429 }
430 }
431}