Skip to main content

banished_game/work/
construction.rs

1//! Construction sites: the building state machine (StateMachineComponent with the ClearArea,
2//! WorkPlace, Build and Residence controllers) and the construction works (`GatherRawMaterials`,
3//! `BuildStructure`). See docs/JOBS.md.
4//!
5//! A placed building's controllers are linked in component order; a state is handled by the last
6//! controller that defines it, and a callback returning false passes it to the previous one. The
7//! callbacks themselves live on [`crate::town::Sim`].
8//!
9//! Original: `FUN_1400aeb30`.
10
11mod machine;
12mod site;
13mod states;
14pub use machine::{run, Machine};
15pub use site::{
16    Attendance, BuildWork, ClearWork, Construction, Decal, Gather, PickupWork, Pile, ProduceState,
17    ProduceTemplate, ProduceWork, Recipe, Site, SiteTemplate,
18};
19pub use states::StateDef;
20
21use std::collections::{BTreeMap, BTreeSet};
22
23use crate::work::jobs::Jobs;
24use crate::work::professions::{Command, Professions, Workforce};
25
26/// The ControllerComponents the port has state tables for.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Controller {
29    /// Clears the building's area before construction.
30    ///
31    /// Original: `FUN_14011d4e0`.
32    ClearArea,
33    /// The workplace: workers and the work while the building works.
34    ///
35    /// Original: `FUN_140134870`.
36    WorkPlace,
37    /// Construction: materials, building work and the stages.
38    ///
39    /// Original: `FUN_14010e560`.
40    Build,
41    /// A home.
42    ///
43    /// Original: `FUN_14012f820`.
44    Residence,
45    /// ClearResourceComponent: the removal marker.
46    ///
47    /// Original: `FUN_140032150`, `FUN_140032270`.
48    ClearResource,
49    /// ResourceProduceComponent, here ConsumeProduceComponent.
50    ///
51    /// Original: `FUN_140130700`.
52    ResourceProduce,
53    /// AttendanceComponent: school, chapel, doctor.
54    ///
55    /// Original: `FUN_140044f90`.
56    Attendance,
57    /// HealerComponent: the herbalist.
58    ///
59    /// Original: `FUN_14004b980`.
60    Healer,
61    /// HappinessComponent.
62    ///
63    /// Original: `FUN_14004b570`.
64    Happiness,
65    /// CropFieldComponent.
66    ///
67    /// Original: `FUN_140120620`.
68    CropField,
69    /// OrchardComponent.
70    ///
71    /// Original: `FUN_140052a40`.
72    Orchard,
73    /// PastureComponent.
74    ///
75    /// Original: `FUN_14012d660`.
76    Pasture,
77    /// AbsorbResourceComponent: a storage yard takes in the piles on its footprint.
78    ///
79    /// Original: `FUN_14002f5d0`.
80    AbsorbResource,
81    /// StorageLocationComponent: a town storage.
82    ///
83    /// Original: `FUN_1401328e0`.
84    StorageLocation,
85    /// CemeteryComponent.
86    ///
87    /// Original: `FUN_140048100`.
88    Cemetery,
89    /// MarketLocationComponent: a town market.
90    ///
91    /// Original: `FUN_14004fa70`.
92    MarketLocation,
93    /// DestroyComponent: demolition, damage and rubble.
94    ///
95    /// Original: `FUN_1401231d0`.
96    Destroy,
97    /// WellComponent: a water source for firefighters.
98    ///
99    /// Original: `FUN_14005da70`.
100    Well,
101    /// ImmigrationComponent: the town hall's nomads.
102    ///
103    /// Original: `FUN_14004f020`.
104    Immigration,
105    /// TradeComponent: the trading post's merchants.
106    ///
107    /// Original: `FUN_14005cfb0`.
108    Trade,
109    /// MultiObjectComponent: a bridge's or a pasture's parts.
110    ///
111    /// Original: `FUN_14012b000`.
112    MultiObject,
113}
114
115// exit +0x20; DAT_1401dbca0; +8; +0x18
116/// A state callback (records: enter, scheduled update, transition functions).
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub enum Callback {
119    /// No callback.
120    None,
121    /// The clear-area controller: entering states 0 and 21.
122    ///
123    /// Original: `FUN_14011ce30`, `FUN_14011cdc0`, `FUN_14011cd60`.
124    ClearEnter0,
125    /// The clear-area controller: the scheduled update of states 0 and 21.
126    ClearUpdate0,
127    /// The clear-area controller: leaving state 0.
128    ClearExit0,
129    /// The clear-area controller: leaving state 21.
130    ///
131    /// Original: `FUN_14011cc40` (state 0x15 exit).
132    ClearExit15,
133    /// The clear-area controller: entering state 1.
134    ///
135    /// Original: `FUN_14011ccf0`, `FUN_14011ccc0`.
136    ClearEnter1,
137    /// The clear-area controller: entering state 2.
138    ClearEnter2,
139    /// The workplace controller: entering state 0.
140    ///
141    /// Original: `FUN_140134310` → `FUN_1401349f0`; `FUN_1401342c0`; `FUN_140134230`.
142    WorkPlaceEnter0,
143    /// The workplace controller: entering state 2.
144    WorkPlaceEnter2,
145    /// The workplace controller: entering state 3.
146    WorkPlaceEnter3,
147    /// The build controller: entering state 1.
148    ///
149    /// Original: `FUN_14010d760`, `FUN_14010d6b0`.
150    BuildEnter1,
151    /// The build controller: the scheduled update of state 1.
152    BuildUpdate1,
153    /// The build controller: entering state 8.
154    ///
155    /// Original: `FUN_14010cfb0`, `FUN_14010cbb0`, `FUN_14010cad0`.
156    BuildEnter8,
157    /// The build controller: the scheduled update of state 8.
158    BuildUpdate8,
159    /// The build controller: leaving state 8.
160    BuildExit8,
161    /// The build controller: the transition from state 8 to 2, when construction ends.
162    ///
163    /// Original: `FUN_14010cab0` → `FUN_14010d4c0`.
164    BuildFinish,
165    /// The residence: entering state 2.
166    ///
167    /// Original: `FUN_14012f2b0`.
168    ResidenceEnter2,
169    /// The marker destroys its entity once its cell is clear.
170    ///
171    /// Original: `FUN_14011da30`.
172    ClearResourceEnter2,
173    /// Built → producing (0xd), or idle (0xe) when switched off.
174    ///
175    /// Original: `FUN_140130680`.
176    ProduceEnter2,
177    /// The transition from state 2 to 0xd: the cells of the output points become empty ground.
178    ///
179    /// Original: `FUN_140130460`.
180    ProduceClearPoints,
181    /// Register the production work and enable the workplace (0xe when switched off).
182    ///
183    /// Original: `FUN_140130330`.
184    ProduceEnterD,
185    /// Remove the work and disable the workplace.
186    ///
187    /// Original: `FUN_1401301f0`.
188    ProduceEnterE,
189    // +0xa8
190    /// State 0xd's scheduled update → slot (the dock's water).
191    ///
192    /// Original: `FUN_140130660`.
193    ProduceUpdateD,
194    // +0xa0
195    /// Transition 0xd → 3 → slot (the dock leaves the map layer).
196    ///
197    /// Original: `FUN_140130440`.
198    ProduceLeaveD,
199    /// Register the attendance, its LocationWork, enable the workplace.
200    ///
201    /// Original: `FUN_1400445f0`.
202    AttendanceEnter2,
203    /// Status icon 2 when every attendance of the kind is full.
204    ///
205    /// Original: `FUN_140044590`.
206    AttendanceExit2,
207    /// Unregister, send the attendees away, disable the workplace.
208    ///
209    /// Original: `FUN_140044530`.
210    AttendanceEnter3,
211    /// Register / unregister the healer.
212    ///
213    /// Original: `FUN_14004b790`, `FUN_14004b750`.
214    HealerEnter2,
215    /// A healer: entering state 3.
216    HealerEnter3,
217    /// A happiness building: entering its active states.
218    ///
219    /// Original: `FUN_14004b350` (states 2 and 0xd enter), `FUN_14004b300` (their scheduled
220    /// update), `FUN_14004b2a0` (state 3 enter).
221    HappinessEnter,
222    /// A happiness building: the scheduled update of its active states.
223    HappinessUpdate,
224    /// A happiness building: leaving its active states.
225    HappinessLeave,
226    /// A crop field: entering state 2.
227    ///
228    /// Original: `FUN_14011fdf0` / `FUN_14011fd30` (state 2), `FUN_14011f7e0` / `FUN_14011f6b0`
229    /// (9), `FUN_14011f5d0` / `FUN_14011f440` / `FUN_14011f4a0` (10), `FUN_14011f330` /
230    /// `FUN_14011f250` (11).
231    CropEnter2,
232    /// A crop field: the scheduled update of state 2.
233    CropUpdate2,
234    /// A crop field: entering state 9.
235    CropEnter9,
236    /// A crop field: the scheduled update of state 9.
237    CropUpdate9,
238    /// A crop field: entering state 10.
239    CropEnter10,
240    /// A crop field: leaving state 10.
241    CropExit10,
242    /// A crop field: the scheduled update of state 10.
243    CropUpdate10,
244    /// A crop field: entering state 11.
245    CropEnter11,
246    /// A crop field: the scheduled update of state 11.
247    CropUpdate11,
248    // +0x10
249    /// The market joins / leaves the town's.
250    ///
251    /// Original: `FUN_14004f880`, `FUN_14004f840`.
252    MarketEnter2,
253    /// A market: entering state 3.
254    MarketEnter3,
255    /// The cemetery joins / leaves the town's.
256    ///
257    /// Original: `FUN_140047920`, `FUN_1400478e0`.
258    CemeteryEnter2,
259    /// A cemetery: entering state 3.
260    CemeteryEnter3,
261    /// AbsorbResource's state 0 update.
262    ///
263    /// Original: `FUN_14010ae20`.
264    AbsorbUpdate0,
265    /// Join / leave the town's storages.
266    ///
267    /// Original: `FUN_140132700`, `FUN_1401326c0`.
268    StorageEnter2,
269    /// A storage: entering state 3.
270    StorageEnter3,
271    /// A pasture: entering state 2.
272    ///
273    /// Original: `FUN_14012c590` / `FUN_14012bcb0` (state 2), `FUN_14012c310` (3).
274    PastureEnter2,
275    /// A pasture: the scheduled update of state 2.
276    PastureUpdate2,
277    /// A pasture: entering state 3.
278    PastureEnter3,
279    /// An orchard: entering state 2.
280    ///
281    /// Original: `FUN_140051e90` / `FUN_140051e20` (2), `FUN_1400519d0` / `FUN_1400518f0` (9),
282    /// `FUN_140051790` / `FUN_140051560` (10), `FUN_140051500` / `FUN_140051310` (exits of 10 and
283    /// 12), `FUN_140051440` / `FUN_140051370` (12), `FUN_140051160` / `FUN_140050fb0` (11),
284    /// `FUN_140050e40` / `FUN_140050d40` / `FUN_140050d90` (0x14).
285    OrchardEnter2,
286    /// An orchard: the scheduled update of state 2.
287    OrchardUpdate2,
288    /// An orchard: entering state 9.
289    OrchardEnter9,
290    /// An orchard: the scheduled update of state 9.
291    OrchardUpdate9,
292    /// An orchard: entering state 10.
293    OrchardEnter10,
294    /// An orchard: the scheduled update of state 10.
295    OrchardUpdate10,
296    /// An orchard: leaving states 10 and 12, when the work ends.
297    OrchardExitWork,
298    /// An orchard: entering state 12.
299    OrchardEnter12,
300    /// An orchard: the scheduled update of state 12.
301    OrchardUpdate12,
302    /// An orchard: entering state 11.
303    OrchardEnter11,
304    /// An orchard: the scheduled update of state 11.
305    OrchardUpdate11,
306    /// An orchard: entering state 20.
307    OrchardEnter14,
308    /// An orchard: leaving state 20.
309    OrchardExit14,
310    /// An orchard: the scheduled update of state 20.
311    OrchardUpdate14,
312    /// The families move out and the residence leaves the town's.
313    ///
314    /// Original: `FUN_14012edf0`.
315    ResidenceEnter3,
316    /// Demolition: entering state 3.
317    ///
318    /// Original: `FUN_140122c80` (3), `FUN_1401224a0` / `FUN_140122390` (4), `FUN_140122140` /
319    /// `FUN_1401215f0` (5), `FUN_1401229a0` / `FUN_140122960` / `FUN_1401228d0` (6).
320    DestroyEnter3,
321    /// Demolition: entering state 4.
322    DestroyEnter4,
323    /// Demolition: the scheduled update of state 4.
324    DestroyUpdate4,
325    /// Demolition: entering state 5.
326    DestroyEnter5,
327    /// Demolition: the scheduled update of state 5.
328    DestroyUpdate5,
329    /// Demolition: entering state 6.
330    DestroyEnter6,
331    /// Demolition: the scheduled update of state 6.
332    DestroyUpdate6,
333    /// Demolition: leaving state 6.
334    DestroyExit6,
335    /// The well joins / leaves the town's water sources.
336    ///
337    /// Original: `FUN_14005d880`, `FUN_14005d840`.
338    WellEnter2,
339    /// The well leaves the town's water sources.
340    WellEnter3,
341    /// The town hall's immigration: entering state 2.
342    ///
343    /// Original: `FUN_14004dbf0` / `FUN_14004db80` (2), `FUN_14004db00` / `FUN_14004da30` (0xf),
344    /// `FUN_14004d9f0` / `FUN_14004d9d0` (0x10), `FUN_14004d810` / `FUN_14004d760` (0x11),
345    /// `FUN_14004d660` (0x10 and 0x11 → 3).
346    ImmigrationEnter2,
347    /// The town hall's immigration: the scheduled update of state 2.
348    ImmigrationUpdate2,
349    /// The town hall's immigration: entering state 15.
350    ImmigrationEnterF,
351    /// The town hall's immigration: the scheduled update of state 15.
352    ImmigrationUpdateF,
353    /// The town hall's immigration: entering state 16.
354    ImmigrationEnter10,
355    /// The town hall's immigration: the scheduled update of state 16.
356    ImmigrationUpdate10,
357    /// The town hall's immigration: entering state 17.
358    ImmigrationEnter11,
359    /// The town hall's immigration: the scheduled update of state 17.
360    ImmigrationUpdate11,
361    /// The town hall's immigration: leaving its active states.
362    ImmigrationLeave,
363    /// The trading post: entering state 2.
364    ///
365    /// Original: `FUN_14005b530` / `FUN_14005b3c0` (2), `FUN_14005b3b0` (0x12), `FUN_14005b160` /
366    /// `FUN_14005b030` / `FUN_14005b0e0` (0x13), `FUN_14005bac0` (→ 3).
367    TradeEnter2,
368    /// The trading post: the scheduled update of state 2.
369    TradeUpdate2,
370    /// The trading post: entering state 18.
371    TradeEnter12,
372    /// The trading post: entering state 19.
373    TradeEnter13,
374    /// The trading post: leaving state 19.
375    TradeExit13,
376    /// The trading post: the scheduled update of state 19.
377    TradeUpdate13,
378    /// The trading post: leaving its active states.
379    TradeLeave,
380    /// Activate the parts' MapComponents and floors.
381    ///
382    /// Original: `FUN_14012aa30`.
383    MultiObjectEnter2,
384    /// Callbacks whose port is still missing (they pass the state on).
385    Missing,
386}
387
388use Callback as C;
389
390/// What a callback needs to run, borrowed from the town (see [`crate::town::Sim`]).
391pub trait Callbacks {
392    /// Run a callback. Returns false to pass the state on to the previous controller.
393    fn call(&mut self, jobs: &mut Jobs, site: usize, f: Callback) -> bool;
394    /// A site's state machine.
395    fn machine(&mut self, site: usize) -> &mut Machine;
396}
397
398#[cfg(test)]
399mod tests {
400    use super::*;
401
402    struct Host {
403        machine: Machine,
404        log: Vec<Callback>,
405        clear: bool,
406    }
407
408    impl Callbacks for Host {
409        fn call(&mut self, _: &mut Jobs, _: usize, f: Callback) -> bool {
410            if f == C::None {
411                return false;
412            }
413            self.log.push(f);
414            match f {
415                C::ClearEnter0 | C::ClearUpdate0 => {
416                    if self.clear {
417                        self.machine.request(1);
418                    }
419                    f == C::ClearEnter0
420                }
421                C::BuildEnter1 => {
422                    self.machine.request(8);
423                    true
424                }
425                C::BuildEnter8 => {
426                    self.machine.request(2);
427                    true
428                }
429                C::WorkPlaceEnter0 | C::WorkPlaceEnter2 | C::ResidenceEnter2 => false,
430                _ => true,
431            }
432        }
433        fn machine(&mut self, _: usize) -> &mut Machine {
434            &mut self.machine
435        }
436    }
437
438    #[test]
439    fn house_states_run_in_order_through_the_chain() {
440        use Controller::*;
441        let mut h = Host {
442            machine: Machine::new(vec![ClearArea, WorkPlace, Build, Residence]),
443            log: Vec::new(),
444            clear: false,
445        };
446        let mut jobs = Jobs::default();
447        run(&mut h, &mut jobs, 0, false, false);
448        assert_eq!(h.log, vec![C::WorkPlaceEnter0, C::ClearEnter0]);
449        assert_eq!(h.machine.state, 0);
450        h.clear = true;
451        h.log.clear();
452        run(&mut h, &mut jobs, 0, true, true);
453        assert_eq!(
454            h.log,
455            vec![
456                C::ClearUpdate0,
457                C::ClearExit0,
458                C::BuildEnter1,
459                C::BuildEnter8,
460                C::BuildExit8,
461                C::BuildFinish,
462                C::ResidenceEnter2,
463                C::WorkPlaceEnter2,
464                C::ClearEnter2,
465            ]
466        );
467        assert_eq!(h.machine.state, 2);
468    }
469}