1use super::*;
4
5impl Sim<'_, '_> {
6 pub(super) fn pickup_refresh(&mut self, work: &mut Work) {
8 let id = work.id;
9 if let Some(p) = self.piles.get_mut(&work.entity) {
10 if let Some(c) = p.claimer {
11 if self.characters[c].stack.holds_task(id).is_none() {
12 p.claimer = None;
13 p.state = 0;
14 }
15 }
16 }
17 }
18
19 pub(super) fn any_storage_fits(&self, material: usize, count: i32) -> bool {
21 self.storage_sites.iter().any(|s| {
22 let st = &self.storages[s.storage];
23 st.accepts & self.materials[material].flags != 0
24 && st.fits(self.materials, material, count)
25 })
26 }
27
28 pub(super) fn pickup_try(&mut self, work: &mut Work, c: usize, time: i64) -> u8 {
32 let e = work.entity;
33 let Some(pile) = self.piles.get(&e).cloned() else {
34 return 1;
35 };
36 if pile.state != 0 {
37 return 1;
38 }
39 let size = |m: usize, s: &Self| s.materials[m].size;
40 let carried = &self.states[c].carried;
41 let mut free = carried.capacity - carried.used(self.materials);
42 if free < size(pile.material, self) * pile.count {
43 self.drop_off(c);
44 return 2;
45 }
46 let from = self.citizen_cell(c);
47 let cell = work.cell;
48 if !self.any_storage_fits(pile.material, pile.count) {
49 return 1;
50 }
51 let Some(nb) = nearest_neighbour(self.size, &self.cells.types, cell, from, 0xe07) else {
52 return 1;
53 };
54 if !self.reachable(nb, from) {
55 return 1;
56 }
57 if !self.patience(work, c, nb, from, time) {
58 return 0;
59 }
60 let found = self.find_storage(pile.material, pile.count, true, nb);
61 let Some(found) = found else {
62 return 1;
63 };
64 let first_rank = self.rank_or_distance(from, cell);
65 let mut list: Vec<(i32, i32, i32, i32, usize)> = vec![(cell, nb, first_rank, work.id, e)];
66 let n = self.size as i32;
67 let (px, py) = (cell % n, cell / n);
68 for dy in -5..=5 {
69 for dx in -5..=5 {
70 if dx == 0 && dy == 0 {
71 continue;
72 }
73 let (x, y) = (px + dx, py + dy);
74 if x < 0 || y < 0 || x >= n || y >= n {
75 continue;
76 }
77 let k = y * n + x;
78 let Some(o) = self.entity_on(k) else { continue };
79 let Some(other) = self.piles.get(&o).cloned() else {
80 continue;
81 };
82 if !self.storages[found.storage].fits(self.materials, other.material, 1) {
83 continue;
84 }
85 let Some(nb2) = nearest_neighbour(self.size, &self.cells.types, k, from, 0xe07)
86 else {
87 continue;
88 };
89 if !self.reachable(nb2, from) {
90 continue;
91 }
92 if other.work < 0 || other.state != 0 {
93 continue;
94 }
95 let r = self.rank_or_distance(from, k);
96 let id = other.work;
97 list.push((k, nb2, r, id, o));
98 }
99 }
100 crate::behaviour::ai::crt_qsort_by(&mut list, |a, b| a.2 - b.2);
101 let mut first = true;
102 for (k, nb2, _, id, o) in list {
103 let p = &self.piles[&o];
104 let w = size(p.material, self) * p.count;
105 if w > free {
106 continue;
107 }
108 free -= w;
109 let point = self.ground_at(nb2);
110 self.pickup_sequence(
111 c,
112 if first { Some(&mut *work) } else { None },
113 id,
114 o,
115 point,
116 k,
117 );
118 first = false;
119 let p = self.piles.get_mut(&o).unwrap();
120 p.state = 1;
121 p.claimer = Some(c);
122 }
123 self.took_job(c, work);
124 if first {
125 1
126 } else {
127 2
128 }
129 }
130
131 pub(crate) fn find_storage(
133 &mut self,
134 material: usize,
135 count: i32,
136 put: bool,
137 cell: i32,
138 ) -> Option<crate::work::stock::StorageMatch> {
139 let town = TownView {
140 size: self.size,
141 types: &self.cells.types,
142 regions: self.regions,
143 occupants: &self.cells.occupants,
144 objects: &self.cells.objects,
145 floors: &self.cells.floors,
146 entities: self.entities,
147 happiness: self.happiness,
148 };
149 let (size, terrain) = (self.size, self.terrain);
150 let floors = &self.cells.floors;
151 let ground = move |cell: i32| crate::town::floor_ground(floors, size, terrain, cell);
152 let mut geometry = StorageGeometry {
153 town: &town,
154 sectors: self.sectors,
155 ground: &ground,
156 };
157 find_material_storage(
158 self.storage_sites,
159 self.storages,
160 self.materials,
161 material,
162 count,
163 put,
164 cell,
165 None,
166 &mut geometry,
167 self.rng,
168 )
169 }
170
171 pub(crate) fn find_storage_excluding(
173 &mut self,
174 material: usize,
175 count: i32,
176 put: bool,
177 cell: i32,
178 exclude: usize,
179 ) -> Option<crate::work::stock::StorageMatch> {
180 let town = TownView {
181 size: self.size,
182 types: &self.cells.types,
183 regions: self.regions,
184 occupants: &self.cells.occupants,
185 objects: &self.cells.objects,
186 floors: &self.cells.floors,
187 entities: self.entities,
188 happiness: self.happiness,
189 };
190 let (size, terrain) = (self.size, self.terrain);
191 let floors = &self.cells.floors;
192 let ground = move |cell: i32| crate::town::floor_ground(floors, size, terrain, cell);
193 let mut geometry = StorageGeometry {
194 town: &town,
195 sectors: self.sectors,
196 ground: &ground,
197 };
198 find_material_storage(
199 self.storage_sites,
200 self.storages,
201 self.materials,
202 material,
203 count,
204 put,
205 cell,
206 Some(exclude),
207 &mut geometry,
208 self.rng,
209 )
210 }
211
212 pub(super) fn pickup_sequence(
215 &mut self,
216 c: usize,
217 current: Option<&mut Work>,
218 work: i32,
219 pile: usize,
220 point: [f32; 3],
221 pile_cell: i32,
222 ) {
223 if let Some(w) = current {
224 self.clear_citizen(c, Some(w));
225 }
226 let facing = crate::behaviour::character::work_facing(self.facing_centre(pile), point);
227 let carrying = self.states[c].carried.used(self.materials) > 0;
228 self.push(c, Action::ShowTool(-1));
229 self.on_empty_think(c);
230 self.push(c, self.perform(c, facing, 7, 0.75, false));
231 if !carrying {
232 self.push(c, Action::ShowTool(-2));
233 }
234 self.push(c, Action::PickupPile { entity: pile });
235 self.push(c, Action::CompleteTask { work, param: 0 });
236 self.push(c, self.perform(c, facing, 6, 0.75, false));
237 let mask = WALKABLE | crate::pathfind::bit(self.cells.types[pile_cell as usize]);
238 let m = MoveTo::new(
239 point,
240 if carrying { 4 } else { 1 },
241 0,
242 mask,
243 0,
244 -1.0,
245 self.rng,
246 );
247 self.push(c, Action::move_to(m));
248 if carrying {
249 self.push(c, Action::ShowTool(-2));
250 }
251 }
252
253 pub fn drop_off(&mut self, c: usize) {
258 let from = self.citizen_cell(c);
259 let items: Vec<(usize, i32)> = self.states[c]
260 .carried
261 .items
262 .iter()
263 .filter(|i| i.count != 0)
264 .map(|i| (i.material, i.count))
265 .collect();
266 let mut chosen: Vec<(usize, [f32; 3], usize)> = Vec::new();
267 for (material, count) in items {
268 if chosen
269 .iter()
270 .any(|&(s, _, _)| self.storages[s].fits(self.materials, material, count))
271 {
272 continue;
273 }
274 if let Some(f) = self.find_storage(material, count, true, from) {
275 let entity = self.storage_sites[f.site].entity;
276 chosen.push((f.storage, f.target, entity));
277 }
278 }
279 if chosen.is_empty() {
280 return;
281 }
282 self.clear_citizen(c, None);
283 self.on_empty_think(c);
284 self.push(
287 c,
288 Action::DropOff {
289 storage: usize::MAX,
290 material: usize::MAX,
291 count: -1,
292 redirect: true,
293 },
294 );
295 let mask = WALKABLE | crate::pathfind::bit(self.cells.types[from as usize]);
296 for (storage, target, entity) in chosen {
297 let facing =
298 crate::behaviour::character::work_facing(self.facing_centre(entity), target);
299 self.push(c, self.perform(c, facing, 7, 0.75, false));
300 self.push(c, Action::ShowTool(-2));
301 self.push(
302 c,
303 Action::DropOff {
304 storage,
305 material: usize::MAX,
306 count: -1,
307 redirect: false,
308 },
309 );
310 self.push(c, self.perform(c, facing, 6, 0.75, false));
311 let m = MoveTo::new(target, 4, 0, mask, 0, -1.0, self.rng);
312 self.push(c, Action::move_to(m));
313 }
314 let s = &mut self.states[c];
315 s.job = -1;
316 s.health_events |= 4;
317 s.supply_activity = 0;
318 }
319}
320
321impl Sim<'_, '_> {
322 pub fn residence_clear_storage(&mut self, jobs: &mut Jobs, visits: &[(usize, i64)]) {
327 for &(e, _) in visits {
328 if !self.homes.contains_key(&e) {
329 continue;
330 }
331 let Some(site) = self.sites.get(&e) else {
332 continue;
333 };
334 let storage = site.storage;
335 let registered = site.work_id >= 0 && jobs.works.contains_key(&site.work_id);
336 let residents: usize = self.homes[&e].families.iter().map(Vec::len).sum();
337 let used = self.storages[storage].used(self.materials);
338 if residents == 0 && used != 0 && !registered {
339 let work = Work::new(
340 e,
341 self.rect_cell(e),
342 self.professions.laborer,
343 WorkKind::ClearStorage(crate::production::herbalist::HerbalistWork::default()),
344 );
345 self.set_work(jobs, e, work, 1, false);
346 } else if registered && used == 0 {
347 self.remove_work(jobs, e);
348 }
349 }
350 }
351
352 pub(crate) fn clear_storage_try(&mut self, work: &mut Work, c: usize, time: i64) -> u8 {
356 let e = work.entity;
357 let Some(storage) = self.sites.get(&e).map(|s| s.storage) else {
358 return 1;
359 };
360 let here = self.citizen_cell(c);
361 let items: Vec<(usize, i32)> = self.storages[storage]
362 .items
363 .iter()
364 .map(|i| (i.material, i.count - i.reserved))
365 .collect();
366 for (k, (material, left)) in items.into_iter().enumerate() {
367 if left <= 0 {
368 continue;
369 }
370 let mut amount = left;
371 let size = self.materials[material].size.max(1);
372 let carried = &self.states[c].carried;
373 let free = carried.capacity - carried.used(self.materials);
374 if free < size * amount {
375 amount = free / size;
376 }
377 if amount == 0 {
378 self.drop_off(c);
380 return 2;
381 }
382 let Some(found) = self.find_storage_excluding(material, amount, true, work.cell, e)
383 else {
384 continue;
385 };
386 if !self.reachable(here, work.cell) {
387 return 1;
388 }
389 if self.use_point_count(e) == 0 {
391 return 1;
392 }
393 let (source, _) = self.use_point(e, 0);
394 let source_cell = self.cell_of(source);
395 if !self.patience(work, c, source_cell, here, time) {
396 return 0;
397 }
398 let target_entity = self.storage_sites[found.site].entity;
399 self.haul(
400 c,
401 work,
402 Haul {
403 target_entity,
404 target_storage: found.storage,
405 target: found.target,
406 source_storage: storage,
407 source_entity: e,
408 source,
409 material,
410 amount,
411 param: (amount << 8) | k as i32,
412 partial: false,
413 complete_on_pickup: true,
414 ..Default::default()
415 },
416 );
417 self.took_job(c, work);
418 if let WorkKind::ClearStorage(h) = &mut work.kind {
419 h.workers.insert(c);
420 }
421 return 2;
422 }
423 1
424 }
425}