Skip to main content

banished_game/work/construction/
machine.rs

1//! The state machine that runs a site's controllers: the current state of each, messages to them,
2//! and one step of the machine.
3
4use super::*;
5
6// StateMachineComponent +0x48; +0; +8; +9; +10; +0xb
7/// The runner's fields (`StateMachineComponent`): the current interface, state, its index, pending
8/// state, pending transition.
9#[derive(Debug, Clone, Default)]
10pub struct Machine {
11    /// The building's controllers, in component order.
12    pub controllers: Vec<Controller>,
13    /// The controller that handles the current state.
14    pub current: usize,
15    /// The current state.
16    pub state: i8,
17    /// The index of the current state in its controller's table.
18    pub index: i8,
19    /// The state to enter next (-1: none).
20    pub pending: i8,
21    /// The transition requested (-1: none).
22    pub transition: i8,
23}
24
25/// Run a building's state machine for one visit: handle the pending state change, then the
26/// scheduled or per-iteration update of the current state.
27///
28/// Original: `FUN_1400aeb30(machine, time, scheduled, update)`.
29pub fn run(host: &mut impl Callbacks, jobs: &mut Jobs, site: usize, scheduled: bool, update: bool) {
30    let mut update = update;
31    loop {
32        while host.machine(site).pending >= 0 {
33            let m = host.machine(site);
34            if m.state >= 0 {
35                // The exit chain, from the current interface back.
36                let (state, mut node, mut index) = (m.state, m.current, m.index as i32);
37                loop {
38                    let m = host.machine(site);
39                    let f = if index >= 0 {
40                        m.controllers[node].table()[index as usize].exit
41                    } else {
42                        C::None
43                    };
44                    if index >= 0 && host.call(jobs, site, f) {
45                        break;
46                    }
47                    let Some(prev) = node.checked_sub(1) else {
48                        break;
49                    };
50                    node = prev;
51                    index = host.machine(site).controllers[node]
52                        .find(state)
53                        .map_or(-1, |i| i as i32);
54                }
55                let m = host.machine(site);
56                if m.transition >= 0 && m.index >= 0 {
57                    let f = m.controllers[m.current].table()[m.index as usize].transitions
58                        [m.transition as usize]
59                        .1;
60                    host.call(jobs, site, f);
61                }
62            }
63            let m = host.machine(site);
64            m.state = m.pending;
65            let last = m.controllers.len() - 1;
66            let Some((node, index)) = m.find_from(last, m.state) else {
67                m.pending = -1;
68                m.transition = -1;
69                m.index = -1;
70                break;
71            };
72            m.current = node;
73            m.index = index as i8;
74            m.pending = -1;
75            m.transition = -1;
76            let state = m.state;
77            // The enter chain.
78            let (mut node, mut index) = (node, index as i32);
79            loop {
80                let f = if index >= 0 {
81                    host.machine(site).controllers[node].table()[index as usize].enter
82                } else {
83                    C::None
84                };
85                if index >= 0 && host.call(jobs, site, f) {
86                    break;
87                }
88                let Some(prev) = node.checked_sub(1) else {
89                    break;
90                };
91                node = prev;
92                index = host.machine(site).controllers[node]
93                    .find(state)
94                    .map_or(-1, |i| i as i32);
95            }
96        }
97        if update && host.machine(site).state >= 0 {
98            update = false;
99            if scheduled {
100                let m = host.machine(site);
101                let (state, mut node, mut index) = (m.state, m.current, m.index as i32);
102                loop {
103                    let f = if index >= 0 {
104                        host.machine(site).controllers[node].table()[index as usize].update
105                    } else {
106                        C::None
107                    };
108                    if index >= 0 && host.call(jobs, site, f) {
109                        break;
110                    }
111                    let Some(prev) = node.checked_sub(1) else {
112                        break;
113                    };
114                    node = prev;
115                    index = host.machine(site).controllers[node]
116                        .find(state)
117                        .map_or(-1, |i| i as i32);
118                }
119            }
120            // The bit-4 update slot (`+0x10`) is null for every ported controller.
121        }
122        if host.machine(site).pending < 0 {
123            return;
124        }
125    }
126}
127
128impl Machine {
129    /// Pending = the first controller's initial state.
130    ///
131    /// Original: `FUN_140130f00`, `FUN_140130b70`.
132    pub fn new(controllers: Vec<Controller>) -> Machine {
133        let pending = controllers.first().map_or(-1, |c| c.initial());
134        Machine {
135            current: controllers.len().saturating_sub(1),
136            controllers,
137            state: -1,
138            index: -1,
139            pending,
140            transition: -1,
141        }
142    }
143
144    /// The last controller (from `node`, going back) that has `state`.
145    pub(super) fn find_from(&self, mut node: usize, state: i8) -> Option<(usize, usize)> {
146        loop {
147            if let Some(i) = self.controllers[node].find(state) {
148                return Some((node, i));
149            }
150            node = node.checked_sub(1)?;
151        }
152    }
153
154    /// Look `msg` up in the current state's transitions.
155    ///
156    /// Original: `FUN_1400aef90`.
157    pub fn request(&mut self, msg: i8) {
158        if self.index < 0 {
159            return;
160        }
161        let def = &self.controllers[self.current].table()[self.index as usize];
162        if let Some(t) = def.transitions.iter().position(|t| t.0 == msg) {
163            self.transition = t as i8;
164            self.pending = msg;
165        }
166    }
167}