Skip to main content

banished_game/town/
work_host.rs

1//! The job manager's work slots ([`WorkHost`]): offering, assigning, finishing and cancelling work.
2
3use super::*;
4
5impl WorkHost for Sim<'_, '_> {
6    fn profession(&self, c: usize) -> usize {
7        self.states[c].profession
8    }
9    fn workplace(&self, c: usize) -> Option<usize> {
10        self.states[c].workplace
11    }
12    fn cell(&self, c: usize) -> i32 {
13        self.citizen_cell(c)
14    }
15    fn has_emergency(&self, c: usize) -> bool {
16        let s = &self.states[c];
17        crate::disasters::fire::has_emergency(
18            self.cells,
19            self.placed,
20            s.home,
21            s.workplace,
22            self.characters[c].position,
23        )
24    }
25    fn works_anywhere(&self, p: usize) -> bool {
26        self.professions.works_anywhere(p)
27    }
28    fn rank(&mut self, a: i32, b: i32) -> i32 {
29        self.rank_or_distance(a, b)
30    }
31    fn stock(&self, slot: usize) -> (i32, i32) {
32        self.stock[slot]
33    }
34    fn laborer(&self) -> usize {
35        self.professions.laborer
36    }
37    fn builder(&self) -> usize {
38        self.professions.builder
39    }
40    fn rng(&mut self) -> &mut Random {
41        self.rng
42    }
43    fn take_events(&mut self) -> Vec<JobEvent> {
44        std::mem::take(&mut self.events)
45    }
46
47    /// `FUN_1400f1020` / `FUN_1400ea0b0`.
48    fn refresh(&mut self, work: &mut Work) {
49        let id = work.id;
50        let gone: Vec<usize> = match &work.kind {
51            WorkKind::Gather(g) => g.workers.iter().copied(),
52            WorkKind::Build(b) => b.workers.iter().copied(),
53            WorkKind::Clear(_) => return self.clear_refresh(work),
54            WorkKind::Pickup(_) => return self.pickup_refresh(work),
55            WorkKind::Produce(_) => return self.produce_refresh(work),
56            WorkKind::Fishing(_) => return self.fishing_refresh(work),
57            WorkKind::PlantArea(_) | WorkKind::Harvest(_) | WorkKind::PickFruit(_) => {
58                return self.area_refresh(work)
59            }
60            WorkKind::PlantOrchard(_) => return self.plant_orchard_refresh(work),
61            // `FUN_1400f3620` / `FUN_1400f0130`.
62            WorkKind::Herbalist(h)
63            | WorkKind::Forester(h)
64            | WorkKind::Hunt(h)
65            | WorkKind::TendArea(h)
66            | WorkKind::Pasture(h)
67            | WorkKind::ClearStorage(h)
68            | WorkKind::Fire(h) => h.workers.iter().copied(),
69            // `FUN_1400e9590`: the builder no longer on it.
70            WorkKind::Road(r) => {
71                if let Some(c) = r.builder {
72                    if self.characters[c].stack.holds_task(id).is_none() {
73                        if let WorkKind::Road(r) = &mut work.kind {
74                            r.builder = None;
75                        }
76                    }
77                }
78                return;
79            }
80            // `FUN_1400fafb0`: the haulers no longer on it.
81            WorkKind::Trade(t) => {
82                let gone: Vec<usize> = t
83                    .haulers
84                    .keys()
85                    .copied()
86                    .filter(|&c| self.characters[c].stack.holds_task(id).is_none())
87                    .collect();
88                if let WorkKind::Trade(t) = &mut work.kind {
89                    for c in gone {
90                        t.haulers.remove(&c);
91                    }
92                }
93                return;
94            }
95            // `FUN_1400f4c90`: the worker no longer holds the task.
96            WorkKind::Location(l) => {
97                if let Some(c) = l.worker {
98                    if self.characters[c].stack.holds_task(id).is_none() {
99                        if let WorkKind::Location(l) = &mut work.kind {
100                            l.worker = None;
101                        }
102                    }
103                }
104                return;
105            }
106            // `FUN_1400f51f0`: the vendors no longer on it leave the plans.
107            WorkKind::Market(m) => {
108                let gone: Vec<usize> = m
109                    .plans
110                    .keys()
111                    .copied()
112                    .filter(|&c| self.characters[c].stack.holds_task(id).is_none())
113                    .collect();
114                if let WorkKind::Market(m) = &mut work.kind {
115                    for c in gone {
116                        m.plans.remove(&c);
117                    }
118                }
119                return;
120            }
121            WorkKind::Test(_) => return,
122        }
123        .filter(|&c| self.characters[c].stack.holds_task(id).is_none())
124        .collect();
125        if gone.is_empty() {
126            return;
127        }
128        match &mut work.kind {
129            WorkKind::Gather(g) => {
130                for c in &gone {
131                    g.workers.remove(c);
132                }
133            }
134            WorkKind::Build(b) => {
135                for c in &gone {
136                    b.workers.remove(c);
137                }
138                let held: Vec<i32> = b
139                    .workers
140                    .iter()
141                    .filter_map(|&c| self.characters[c].stack.holds_task(id))
142                    .collect();
143                for (k, o) in b.occupied.iter_mut().enumerate() {
144                    *o = held.contains(&(k as i32));
145                }
146            }
147            WorkKind::Herbalist(h)
148            | WorkKind::Forester(h)
149            | WorkKind::Hunt(h)
150            | WorkKind::TendArea(h)
151            | WorkKind::Pasture(h)
152            | WorkKind::ClearStorage(h)
153            | WorkKind::Fire(h) => {
154                for c in &gone {
155                    h.workers.remove(c);
156                }
157            }
158            WorkKind::Clear(_)
159            | WorkKind::Pickup(_)
160            | WorkKind::Produce(_)
161            | WorkKind::Fishing(_)
162            | WorkKind::PlantArea(_)
163            | WorkKind::Harvest(_)
164            | WorkKind::PlantOrchard(_)
165            | WorkKind::PickFruit(_)
166            | WorkKind::Location(_)
167            | WorkKind::Market(_)
168            | WorkKind::Trade(_)
169            | WorkKind::Road(_)
170            | WorkKind::Test(_) => {}
171        }
172        if let WorkKind::Gather(_) = work.kind {
173            self.cancel(work);
174            if let WorkKind::Gather(g) = &mut work.kind {
175                g.transit.iter_mut().for_each(|t| *t = 0);
176            }
177        }
178    }
179
180    fn try_assign(&mut self, work: &mut Work, c: usize, time: i64) -> u8 {
181        match work.kind {
182            WorkKind::Gather(_) => self.gather_try(work, c, time),
183            WorkKind::Build(_) => self.build_try(work, c),
184            WorkKind::Clear(_) => self.clear_try(work, c, time),
185            WorkKind::Pickup(_) => self.pickup_try(work, c, time),
186            WorkKind::Produce(_) => self.produce_try(work, c, time),
187            WorkKind::Location(_) => self.location_try(work, c),
188            WorkKind::ClearStorage(_) => self.clear_storage_try(work, c, time),
189            WorkKind::Fire(_) => self.fire_try(work, c, time),
190            WorkKind::Trade(_) => self.trade_try(work, c),
191            WorkKind::Road(_) => self.road_try(work, c, time),
192            WorkKind::Market(_) => self.market_try(work, c),
193            WorkKind::Fishing(_) => self.fishing_try(work, c),
194            WorkKind::PlantArea(_) => self.plant_try(work, c),
195            WorkKind::PlantOrchard(_) => self.plant_orchard_try(work, c),
196            WorkKind::TendArea(_) => {
197                if self
198                    .sites
199                    .get(&work.entity)
200                    .is_some_and(|s| s.orchard.is_some())
201                {
202                    self.tend_orchard_try(work, c)
203                } else {
204                    self.tend_try(work, c)
205                }
206            }
207            WorkKind::PickFruit(_) => {
208                let works: BTreeSet<i32> = self.piles.values().map(|p| p.work).collect();
209                if self.auto_pickup_offers(work.entity, |id| works.contains(&id)) {
210                    return NESTED;
211                }
212                self.pick_fruit_try(work, c)
213            }
214            WorkKind::Harvest(_) => {
215                let works: BTreeSet<i32> = self.piles.values().map(|p| p.work).collect();
216                if self.auto_pickup_offers(work.entity, |id| works.contains(&id)) {
217                    return NESTED;
218                }
219                self.harvest_try(work, c)
220            }
221            WorkKind::Herbalist(_)
222            | WorkKind::Forester(_)
223            | WorkKind::Hunt(_)
224            | WorkKind::Pasture(_) => {
225                self.pasture_stage = false;
226                let works: BTreeSet<i32> = self.piles.values().map(|p| p.work).collect();
227                if self.auto_pickup_offers(work.entity, |id| works.contains(&id)) {
228                    return NESTED;
229                }
230                self.resume(work, c)
231            }
232            WorkKind::Test(_) => 0,
233        }
234    }
235
236    fn take_offers(&mut self) -> Vec<i32> {
237        std::mem::take(&mut self.offers)
238    }
239
240    fn offers_again(&mut self) -> bool {
241        std::mem::take(&mut self.offers_again)
242    }
243
244    fn resume(&mut self, work: &mut Work, c: usize) -> u8 {
245        match work.kind {
246            WorkKind::Produce(_) => self.produce_resume(work, c),
247            WorkKind::Herbalist(_) => self.herbalist_try(work, c),
248            WorkKind::Forester(_) => self.forester_try(work, c),
249            WorkKind::Hunt(_) => self.hunt_try(work, c),
250            WorkKind::Harvest(_) => self.harvest_try(work, c),
251            WorkKind::PickFruit(_) => self.pick_fruit_try(work, c),
252            WorkKind::Pasture(_) => self.pasture_try(work, c),
253            _ => 1,
254        }
255    }
256
257    fn complete(&mut self, work: &mut Work, c: usize, param: i32) -> bool {
258        match &mut work.kind {
259            WorkKind::Gather(g) => {
260                g.workers.remove(&c);
261                let i = (param & 0xff) as usize;
262                if let Some(t) = g.transit.get_mut(i) {
263                    *t -= param >> 8;
264                }
265                if let Some(s) = g.stored.get_mut(i) {
266                    *s += param >> 8;
267                }
268            }
269            WorkKind::Build(b) => {
270                b.workers.remove(&c);
271                if let Some(o) = b.occupied.get_mut(param as usize) {
272                    *o = false;
273                }
274            }
275            // `FUN_1400eb300`: the resource (or cell) is done.
276            WorkKind::Clear(cl) => {
277                cl.resources.retain(|_, &mut w| w != c);
278                cl.cells.remove(&param);
279            }
280            // `FUN_1400f8390`.
281            WorkKind::Pickup(_) => {
282                if let Some(p) = self.piles.get_mut(&work.entity) {
283                    p.claimer = None;
284                    p.state = 2;
285                }
286            }
287            WorkKind::Produce(_) => Self::produce_complete(work, param, false),
288            WorkKind::Fishing(_) => Self::fishing_complete(work, param),
289            WorkKind::PlantArea(_) | WorkKind::Harvest(_) => Self::area_complete(work, param),
290            WorkKind::PlantOrchard(_) => Self::plant_orchard_done(work, param),
291            WorkKind::PickFruit(_) => Self::pick_done(work, param, false),
292            // `FUN_1400f4d20`: free for the next one; the work stays.
293            WorkKind::Location(l) => l.worker = None,
294            // `FUN_1400f3860` / `FUN_1400f0370` / `FUN_1400f7820` / `FUN_1400ec100`.
295            WorkKind::Herbalist(h)
296            | WorkKind::Forester(h)
297            | WorkKind::Hunt(h)
298            | WorkKind::TendArea(h)
299            | WorkKind::Pasture(h)
300            | WorkKind::ClearStorage(h)
301            // `FUN_1400eead0`.
302            | WorkKind::Fire(h) => {
303                h.workers.remove(&c);
304            }
305            // `FUN_1400f5430`.
306            WorkKind::Market(m) => {
307                m.plans.remove(&c);
308            }
309            // `FUN_1400fb1f0`.
310            WorkKind::Trade(t) => {
311                t.haulers.remove(&c);
312            }
313            // `FUN_1400e9620`: parameter 1 (the cell built) ends the work.
314            WorkKind::Road(r) => {
315                r.builder = None;
316                return param == 1;
317            }
318            WorkKind::Test(_) => {}
319        }
320        false
321    }
322
323    fn fail(&mut self, work: &mut Work, c: usize, param: i32) {
324        match &mut work.kind {
325            WorkKind::Gather(g) => {
326                g.workers.remove(&c);
327                if let Some(t) = g.transit.get_mut((param & 0xff) as usize) {
328                    *t -= param >> 8;
329                }
330            }
331            WorkKind::Build(b) => {
332                b.workers.remove(&c);
333                if let Some(o) = b.occupied.get_mut(param as usize) {
334                    *o = false;
335                }
336            }
337            // `FUN_1400eb2d0` calls the complete slot.
338            WorkKind::Clear(cl) => {
339                cl.resources.retain(|_, &mut w| w != c);
340                cl.cells.remove(&param);
341            }
342            // `FUN_1400f8350`.
343            WorkKind::Pickup(_) => {
344                if let Some(p) = self.piles.get_mut(&work.entity) {
345                    p.claimer = None;
346                    p.state = 0;
347                }
348            }
349            WorkKind::Produce(_) => Self::produce_complete(work, param, true),
350            // `FUN_1400ef350` calls the complete slot.
351            WorkKind::Fishing(_) => Self::fishing_complete(work, param),
352            WorkKind::PlantArea(_) | WorkKind::Harvest(_) => Self::area_fail(work, param),
353            WorkKind::PlantOrchard(_) => Self::plant_orchard_done(work, param),
354            WorkKind::PickFruit(_) => Self::pick_done(work, param, true),
355            WorkKind::Location(l) => l.worker = None,
356            WorkKind::Pasture(_) => self.pasture_fail(work, c, param),
357            // `FUN_1400f3850` / `FUN_1400f0360` / `FUN_1400ec0f0` / `FUN_1400eeac0`.
358            WorkKind::ClearStorage(h) | WorkKind::Fire(h) => {
359                h.workers.remove(&c);
360            }
361            WorkKind::Herbalist(h)
362            | WorkKind::Forester(h)
363            | WorkKind::Hunt(h)
364            | WorkKind::TendArea(h) => {
365                h.workers.remove(&c);
366            }
367            // `FUN_1400f5420`.
368            WorkKind::Market(m) => {
369                m.plans.remove(&c);
370            }
371            // `FUN_1400fb1e0`.
372            WorkKind::Trade(t) => {
373                t.haulers.remove(&c);
374            }
375            // `FUN_1400e95f0`.
376            WorkKind::Road(r) => r.builder = None,
377            WorkKind::Test(_) => {}
378        }
379    }
380
381    /// `FUN_1400f1950` / `FUN_1400eac70`: clear every worker's stack (their aborts leave the
382    /// set through the fail slot).
383    fn cancel(&mut self, work: &mut Work) {
384        if let WorkKind::Produce(_) = work.kind {
385            return self.produce_cancel(work);
386        }
387        loop {
388            let next = match &work.kind {
389                WorkKind::Gather(g) => g.workers.first().copied(),
390                WorkKind::Build(b) => b.workers.first().copied(),
391                // `FUN_1400ebb20`: every claimer of a resource or cell.
392                WorkKind::Clear(cl) => cl
393                    .resources
394                    .values()
395                    .chain(cl.cells.values())
396                    .next()
397                    .copied(),
398                // `FUN_1400f8ec0`: the claimer, while claimed.
399                WorkKind::Pickup(_) => self
400                    .piles
401                    .get(&work.entity)
402                    .filter(|p| p.state == 1)
403                    .and_then(|p| p.claimer),
404                // `FUN_1400f4fc0`: the worker's stack is cleared.
405                WorkKind::Location(l) => l.worker,
406                // `FUN_1400efd00`: every worker's stack is cleared.
407                WorkKind::Fishing(fw) => fw.workers.iter().flatten().next().copied(),
408                // `FUN_1400f9930` / `FUN_1400f2740`.
409                WorkKind::PlantArea(_)
410                | WorkKind::Harvest(_)
411                | WorkKind::PlantOrchard(_)
412                | WorkKind::PickFruit(_) => Self::area_workers(work).first().copied(),
413                // `FUN_1400f3fb0` / `FUN_1400f0d70` / `FUN_1400f8060`.
414                WorkKind::Herbalist(h)
415                | WorkKind::Forester(h)
416                | WorkKind::Hunt(h)
417                | WorkKind::TendArea(h)
418                | WorkKind::Pasture(h)
419                | WorkKind::ClearStorage(h)
420                // `FUN_1400eee70`.
421                | WorkKind::Fire(h) => h.workers.first().copied(),
422                // `FUN_1400f7170`: every vendor's stack is cleared.
423                WorkKind::Market(m) => m.plans.keys().next().copied(),
424                // `FUN_1400fc0b0`.
425                WorkKind::Trade(t) => t.haulers.keys().next().copied(),
426                // `FUN_1400e9de0`.
427                WorkKind::Road(r) => r.builder,
428                WorkKind::Produce(_) | WorkKind::Test(_) => None,
429            };
430            let Some(c) = next else { return };
431            self.clear_citizen(c, Some(work));
432            // A worker without the task would loop forever in the original; leave it.
433            match &mut work.kind {
434                WorkKind::Gather(g) => {
435                    g.workers.remove(&c);
436                }
437                WorkKind::Build(b) => {
438                    b.workers.remove(&c);
439                }
440                WorkKind::Clear(cl) => {
441                    cl.resources.retain(|_, &mut w| w != c);
442                    cl.cells.retain(|_, &mut w| w != c);
443                }
444                WorkKind::Pickup(_) => {
445                    if let Some(p) = self.piles.get_mut(&work.entity) {
446                        p.claimer = None;
447                        p.state = 0;
448                    }
449                }
450                WorkKind::Location(l) => l.worker = None,
451                WorkKind::Herbalist(h)
452                | WorkKind::Forester(h)
453                | WorkKind::Hunt(h)
454                | WorkKind::TendArea(h)
455                | WorkKind::Pasture(h)
456                | WorkKind::ClearStorage(h)
457                | WorkKind::Fire(h) => {
458                    h.workers.remove(&c);
459                }
460                WorkKind::Fishing(fw) => {
461                    for w in fw.workers.iter_mut().filter(|w| **w == Some(c)) {
462                        *w = None;
463                    }
464                }
465                WorkKind::PlantArea(a)
466                | WorkKind::Harvest(a)
467                | WorkKind::PlantOrchard(a)
468                | WorkKind::PickFruit(a) => {
469                    let cells: Vec<i32> = a
470                        .claims
471                        .iter()
472                        .filter(|(_, &w)| w == c)
473                        .map(|(&i, _)| i)
474                        .collect();
475                    for i in cells {
476                        a.claims.remove(&i);
477                        if let Some(n) = a.need.get_mut(i as usize) {
478                            *n = true;
479                        }
480                    }
481                }
482                WorkKind::Market(m) => {
483                    m.plans.remove(&c);
484                }
485                WorkKind::Trade(t) => {
486                    t.haulers.remove(&c);
487                }
488                WorkKind::Road(r) => r.builder = None,
489                WorkKind::Produce(_) | WorkKind::Test(_) => {}
490            }
491        }
492    }
493}