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}