Skip to main content

banished_game/work/construction/
states.rs

1//! The controllers' state tables (`StateDef`): for each controller, its states in order, the
2//! callback each one runs, and where it goes next.
3
4use super::*;
5
6/// One state of a building's state machine: its callbacks and its transitions.
7pub struct StateDef {
8    /// The state's number.
9    pub id: i8,
10    /// The callback when the state is entered.
11    pub enter: Callback,
12    /// The callback at each scheduled update.
13    pub update: Callback,
14    /// The callback when the state is left.
15    pub exit: Callback,
16    /// The callbacks of transitions: the target state and its callback.
17    pub transitions: &'static [(i8, Callback)],
18}
19
20pub(super) const CLEAR: &[StateDef] = &[
21    StateDef {
22        id: 0,
23        enter: C::ClearEnter0,
24        update: C::ClearUpdate0,
25        exit: C::ClearExit0,
26        transitions: &[(1, C::None), (3, C::None)],
27    },
28    // `FUN_14011ccb0` / `FUN_14011cca0` are thunks of state 0's enter and update.
29    StateDef {
30        id: 0x15,
31        enter: C::ClearEnter0,
32        update: C::ClearUpdate0,
33        exit: C::ClearExit15,
34        transitions: &[(1, C::None), (3, C::None)],
35    },
36    StateDef {
37        id: 1,
38        enter: C::ClearEnter1,
39        update: C::None,
40        exit: C::None,
41        transitions: &[(2, C::None), (3, C::None)],
42    },
43    StateDef {
44        id: 2,
45        enter: C::ClearEnter2,
46        update: C::None,
47        exit: C::None,
48        transitions: &[(3, C::None)],
49    },
50];
51
52pub(super) const WORKPLACE: &[StateDef] = &[
53    StateDef {
54        id: 0,
55        enter: C::WorkPlaceEnter0,
56        update: C::None,
57        exit: C::None,
58        transitions: &[(1, C::None), (3, C::None)],
59    },
60    StateDef {
61        id: 2,
62        enter: C::WorkPlaceEnter2,
63        update: C::None,
64        exit: C::None,
65        transitions: &[(3, C::None)],
66    },
67    StateDef {
68        id: 3,
69        enter: C::WorkPlaceEnter3,
70        update: C::None,
71        exit: C::None,
72        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
73    },
74];
75
76pub(super) const BUILD: &[StateDef] = &[
77    StateDef {
78        id: 1,
79        enter: C::BuildEnter1,
80        update: C::BuildUpdate1,
81        exit: C::None,
82        transitions: &[(8, C::None), (3, C::None)],
83    },
84    StateDef {
85        id: 8,
86        enter: C::BuildEnter8,
87        update: C::BuildUpdate8,
88        exit: C::BuildExit8,
89        transitions: &[(2, C::BuildFinish), (3, C::None)],
90    },
91    StateDef {
92        id: 2,
93        enter: C::None,
94        update: C::None,
95        exit: C::None,
96        transitions: &[(3, C::None)],
97    },
98];
99
100pub(super) const RESIDENCE: &[StateDef] = &[
101    StateDef {
102        id: 2,
103        enter: C::ResidenceEnter2,
104        // `FUN_14012ee40` runs in `Agents::update` (`life::update_home`).
105        update: C::None,
106        exit: C::None,
107        transitions: &[(3, C::None)],
108    },
109    StateDef {
110        id: 3,
111        enter: C::ResidenceEnter3,
112        update: C::None,
113        exit: C::None,
114        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
115    },
116];
117
118/// The state messages (`FUN_140122860` in 6, `FUN_140122770` / `FUN_140122670` in 7,
119/// `FUN_140121190` in 4 and 5) are [`crate::town::Sim::destroy_message`].
120pub(super) const DESTROY: &[StateDef] = &[
121    StateDef {
122        id: 3,
123        enter: C::DestroyEnter3,
124        update: C::None,
125        exit: C::None,
126        transitions: &[(4, C::None), (6, C::None)],
127    },
128    StateDef {
129        id: 6,
130        enter: C::DestroyEnter6,
131        update: C::DestroyUpdate6,
132        exit: C::DestroyExit6,
133        transitions: &[(7, C::None)],
134    },
135    StateDef {
136        id: 7,
137        enter: C::None,
138        update: C::None,
139        exit: C::None,
140        transitions: &[(0, C::None), (8, C::None), (5, C::None)],
141    },
142    StateDef {
143        id: 4,
144        enter: C::DestroyEnter4,
145        update: C::DestroyUpdate4,
146        exit: C::None,
147        transitions: &[(5, C::None), (2, C::None)],
148    },
149    StateDef {
150        id: 5,
151        enter: C::DestroyEnter5,
152        update: C::DestroyUpdate5,
153        exit: C::None,
154        transitions: &[(0, C::None), (2, C::None), (8, C::None), (7, C::None)],
155    },
156];
157
158pub(super) const WELL: &[StateDef] = &[
159    StateDef {
160        id: 2,
161        enter: C::WellEnter2,
162        update: C::None,
163        exit: C::None,
164        transitions: &[(3, C::None)],
165    },
166    StateDef {
167        id: 3,
168        enter: C::WellEnter3,
169        update: C::None,
170        exit: C::None,
171        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
172    },
173];
174
175/// The state messages (6 in 0x10, 7 and 8 in 0x11) are
176/// [`crate::town::Sim::immigration_message`].
177pub(super) const IMMIGRATION: &[StateDef] = &[
178    StateDef {
179        id: 2,
180        enter: C::ImmigrationEnter2,
181        update: C::ImmigrationUpdate2,
182        exit: C::None,
183        transitions: &[(3, C::None), (0xf, C::None)],
184    },
185    StateDef {
186        id: 0xf,
187        enter: C::ImmigrationEnterF,
188        update: C::ImmigrationUpdateF,
189        exit: C::None,
190        transitions: &[(3, C::None), (0x10, C::None), (2, C::None)],
191    },
192    StateDef {
193        id: 0x10,
194        enter: C::ImmigrationEnter10,
195        update: C::ImmigrationUpdate10,
196        exit: C::None,
197        transitions: &[(3, C::ImmigrationLeave), (0x11, C::None)],
198    },
199    StateDef {
200        id: 0x11,
201        enter: C::ImmigrationEnter11,
202        update: C::ImmigrationUpdate11,
203        exit: C::None,
204        transitions: &[(3, C::ImmigrationLeave), (2, C::None)],
205    },
206];
207
208/// The state messages (9 and 0xb in 0x12, 0xa in 0x13) are
209/// [`crate::town::Sim::trade_message`].
210/// `FUN_14012b000` (`FUN_140036a50`, `FUN_140036b70`): state 2 only.
211pub(super) const MULTI_OBJECT: &[StateDef] = &[StateDef {
212    id: 2,
213    enter: C::MultiObjectEnter2,
214    update: C::None,
215    exit: C::None,
216    transitions: &[(3, C::None)],
217}];
218
219pub(super) const TRADE: &[StateDef] = &[
220    StateDef {
221        id: 2,
222        enter: C::TradeEnter2,
223        update: C::TradeUpdate2,
224        exit: C::None,
225        transitions: &[(3, C::TradeLeave), (0x12, C::None)],
226    },
227    StateDef {
228        id: 0x12,
229        enter: C::TradeEnter12,
230        update: C::None,
231        exit: C::None,
232        transitions: &[(3, C::TradeLeave), (0x13, C::None), (2, C::None)],
233    },
234    StateDef {
235        id: 0x13,
236        enter: C::TradeEnter13,
237        update: C::TradeUpdate13,
238        exit: C::TradeExit13,
239        transitions: &[(3, C::TradeLeave), (2, C::None)],
240    },
241];
242
243pub(super) const ATTENDANCE: &[StateDef] = &[
244    StateDef {
245        id: 2,
246        enter: C::AttendanceEnter2,
247        update: C::None,
248        exit: C::AttendanceExit2,
249        transitions: &[(3, C::None)],
250    },
251    StateDef {
252        id: 3,
253        enter: C::AttendanceEnter3,
254        update: C::None,
255        exit: C::None,
256        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
257    },
258];
259
260pub(super) const HEALER: &[StateDef] = &[
261    StateDef {
262        id: 2,
263        enter: C::HealerEnter2,
264        update: C::None,
265        exit: C::None,
266        transitions: &[(3, C::None), (0xd, C::None)],
267    },
268    StateDef {
269        id: 3,
270        enter: C::HealerEnter3,
271        update: C::None,
272        exit: C::None,
273        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
274    },
275];
276
277pub(super) const CROP_FIELD: &[StateDef] = &[
278    StateDef {
279        id: 2,
280        enter: C::CropEnter2,
281        update: C::CropUpdate2,
282        exit: C::None,
283        transitions: &[(3, C::None), (9, C::None)],
284    },
285    StateDef {
286        id: 9,
287        enter: C::CropEnter9,
288        update: C::CropUpdate9,
289        exit: C::None,
290        transitions: &[(3, C::None), (0xa, C::None)],
291    },
292    StateDef {
293        id: 0xa,
294        enter: C::CropEnter10,
295        update: C::CropUpdate10,
296        exit: C::CropExit10,
297        transitions: &[(3, C::None), (0x15, C::None), (0xb, C::None)],
298    },
299    StateDef {
300        id: 0xb,
301        enter: C::CropEnter11,
302        update: C::CropUpdate11,
303        exit: C::None,
304        transitions: &[(3, C::None), (0x15, C::None)],
305    },
306];
307
308pub(super) const ORCHARD: &[StateDef] = &[
309    StateDef {
310        id: 2,
311        enter: C::OrchardEnter2,
312        update: C::OrchardUpdate2,
313        exit: C::None,
314        transitions: &[(3, C::None), (9, C::None)],
315    },
316    StateDef {
317        id: 9,
318        enter: C::OrchardEnter9,
319        update: C::OrchardUpdate9,
320        exit: C::None,
321        transitions: &[(3, C::None), (0xa, C::None), (0x14, C::None)],
322    },
323    StateDef {
324        id: 0xa,
325        enter: C::OrchardEnter10,
326        update: C::OrchardUpdate10,
327        exit: C::OrchardExitWork,
328        transitions: &[
329            (3, C::None),
330            (0xc, C::None),
331            (0xb, C::None),
332            (0x14, C::None),
333        ],
334    },
335    StateDef {
336        id: 0xc,
337        enter: C::OrchardEnter12,
338        update: C::OrchardUpdate12,
339        exit: C::OrchardExitWork,
340        transitions: &[
341            (3, C::None),
342            (0xa, C::None),
343            (0xb, C::None),
344            (0x14, C::None),
345        ],
346    },
347    StateDef {
348        id: 0xb,
349        enter: C::OrchardEnter11,
350        update: C::OrchardUpdate11,
351        exit: C::None,
352        transitions: &[(3, C::None), (0xa, C::None), (0x14, C::None)],
353    },
354    StateDef {
355        id: 0x14,
356        enter: C::OrchardEnter14,
357        update: C::OrchardUpdate14,
358        exit: C::OrchardExit14,
359        transitions: &[(3, C::None), (9, C::None)],
360    },
361];
362
363pub(super) const MARKET_LOCATION: &[StateDef] = &[
364    StateDef {
365        id: 2,
366        enter: C::MarketEnter2,
367        update: C::None,
368        exit: C::None,
369        transitions: &[(3, C::None)],
370    },
371    StateDef {
372        id: 3,
373        enter: C::MarketEnter3,
374        update: C::None,
375        exit: C::None,
376        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
377    },
378];
379
380pub(super) const CEMETERY: &[StateDef] = &[
381    StateDef {
382        id: 2,
383        enter: C::CemeteryEnter2,
384        update: C::None,
385        exit: C::None,
386        transitions: &[(3, C::None)],
387    },
388    StateDef {
389        id: 3,
390        enter: C::CemeteryEnter3,
391        update: C::None,
392        exit: C::None,
393        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
394    },
395];
396
397pub(super) const ABSORB_RESOURCE: &[StateDef] = &[StateDef {
398    id: 0,
399    enter: C::None,
400    update: C::AbsorbUpdate0,
401    exit: C::None,
402    transitions: &[],
403}];
404
405pub(super) const STORAGE_LOCATION: &[StateDef] = &[
406    StateDef {
407        id: 2,
408        enter: C::StorageEnter2,
409        update: C::None,
410        exit: C::None,
411        transitions: &[(3, C::None)],
412    },
413    StateDef {
414        id: 3,
415        enter: C::StorageEnter3,
416        update: C::None,
417        exit: C::None,
418        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
419    },
420];
421
422pub(super) const PASTURE: &[StateDef] = &[
423    StateDef {
424        id: 2,
425        enter: C::PastureEnter2,
426        update: C::PastureUpdate2,
427        exit: C::None,
428        transitions: &[(3, C::None)],
429    },
430    StateDef {
431        id: 3,
432        enter: C::PastureEnter3,
433        update: C::None,
434        exit: C::None,
435        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
436    },
437];
438
439pub(super) const HAPPINESS: &[StateDef] = &[
440    StateDef {
441        id: 2,
442        enter: C::HappinessEnter,
443        update: C::HappinessUpdate,
444        exit: C::None,
445        transitions: &[(3, C::None)],
446    },
447    StateDef {
448        id: 0xd,
449        enter: C::HappinessEnter,
450        update: C::HappinessUpdate,
451        exit: C::None,
452        transitions: &[(3, C::None)],
453    },
454    StateDef {
455        id: 3,
456        enter: C::HappinessLeave,
457        update: C::None,
458        exit: C::None,
459        transitions: &[(6, C::None), (4, C::None), (5, C::None)],
460    },
461];
462
463pub(super) const CLEAR_RESOURCE: &[StateDef] = &[StateDef {
464    id: 2,
465    enter: C::ClearResourceEnter2,
466    update: C::None,
467    exit: C::None,
468    transitions: &[(3, C::None)],
469}];
470
471pub(super) const RESOURCE_PRODUCE: &[StateDef] = &[
472    StateDef {
473        id: 2,
474        enter: C::ProduceEnter2,
475        update: C::None,
476        exit: C::None,
477        transitions: &[(0xd, C::ProduceClearPoints), (0xe, C::None), (3, C::None)],
478    },
479    StateDef {
480        id: 0xd,
481        enter: C::ProduceEnterD,
482        update: C::ProduceUpdateD,
483        exit: C::None,
484        transitions: &[(0xe, C::None), (3, C::ProduceLeaveD)],
485    },
486    StateDef {
487        id: 0xe,
488        enter: C::ProduceEnterE,
489        update: C::None,
490        exit: C::None,
491        transitions: &[(0xd, C::None), (3, C::None)],
492    },
493];
494
495impl Controller {
496    /// The states of a controller.
497    pub fn table(self) -> &'static [StateDef] {
498        match self {
499            Controller::ClearArea => CLEAR,
500            Controller::WorkPlace => WORKPLACE,
501            Controller::Build => BUILD,
502            Controller::Residence => RESIDENCE,
503            Controller::ClearResource => CLEAR_RESOURCE,
504            Controller::ResourceProduce => RESOURCE_PRODUCE,
505            Controller::Attendance => ATTENDANCE,
506            Controller::Healer => HEALER,
507            Controller::Happiness => HAPPINESS,
508            Controller::CropField => CROP_FIELD,
509            Controller::Orchard => ORCHARD,
510            Controller::Pasture => PASTURE,
511            Controller::AbsorbResource => ABSORB_RESOURCE,
512            Controller::StorageLocation => STORAGE_LOCATION,
513            Controller::Cemetery => CEMETERY,
514            Controller::MarketLocation => MARKET_LOCATION,
515            Controller::Destroy => DESTROY,
516            Controller::Well => WELL,
517            Controller::Immigration => IMMIGRATION,
518            Controller::Trade => TRADE,
519            Controller::MultiObject => MULTI_OBJECT,
520        }
521    }
522
523    // +0x80
524    /// The initial state the first controller gives (ClearArea `0`, Build `1`).
525    ///
526    /// Original: `FUN_140003730`, `FUN_14002fc50`.
527    pub fn initial(self) -> i8 {
528        match self {
529            Controller::Build => 1,
530            _ => 0,
531        }
532    }
533
534    /// Interface slot `+0`: the state's index.
535    pub fn find(self, state: i8) -> Option<usize> {
536        self.table().iter().position(|d| d.id == state)
537    }
538}