Skip to main content

macroquad_ply/experimental/
state_machine.rs

1use crate::experimental::coroutines::Coroutine;
2
3use crate::experimental::scene;
4
5type UpdateFn<T> = Box<dyn FnMut(&mut scene::RefMut<T>, f32)>;
6type CoroutineFn<T> = Box<dyn FnMut(&mut scene::RefMut<T>) -> Coroutine>;
7type OnEndFn<T> = Box<dyn FnMut(&mut scene::RefMut<T>)>;
8
9pub struct State<T: 'static> {
10    update: Option<UpdateFn<T>>,
11    coroutine: Option<CoroutineFn<T>>,
12    on_end: Option<OnEndFn<T>>,
13}
14
15impl<T> State<T> {
16    pub fn new() -> Self {
17        State {
18            update: None,
19            coroutine: None,
20            on_end: None,
21        }
22    }
23
24    pub fn update(self, update: impl FnMut(&mut scene::RefMut<T>, f32) + 'static) -> Self {
25        State {
26            update: Some(Box::new(update)),
27            ..self
28        }
29    }
30
31    pub fn coroutine(
32        self,
33        coroutine: impl FnMut(&mut scene::RefMut<T>) -> Coroutine + 'static,
34    ) -> Self {
35        State {
36            coroutine: Some(Box::new(coroutine)),
37            ..self
38        }
39    }
40
41    pub fn on_end(self, on_end: impl FnMut(&mut scene::RefMut<T>) + 'static) -> Self {
42        State {
43            on_end: Some(Box::new(on_end)),
44            ..self
45        }
46    }
47}
48
49pub enum StateMachine<T: 'static> {
50    Ready(StateMachineOwned<T>),
51    InUse {
52        next_state: Option<usize>,
53        current_state: usize,
54    },
55}
56
57impl<T: scene::Node + 'static> StateMachine<T> {
58    pub fn new() -> StateMachine<T> {
59        StateMachine::Ready(StateMachineOwned::new())
60    }
61
62    pub fn add_state(&mut self, id: usize, state: State<T>) {
63        match self {
64            StateMachine::Ready(state_machine) => state_machine.insert(id, state),
65            _ => panic!(),
66        }
67    }
68
69    pub fn take(&mut self) -> StateMachineOwned<T> {
70        let current_state = self.state();
71        match std::mem::replace(
72            self,
73            StateMachine::InUse {
74                next_state: None,
75                current_state,
76            },
77        ) {
78            StateMachine::InUse { .. } => panic!(),
79            StateMachine::Ready(state_machine) => state_machine,
80        }
81    }
82
83    fn put_back(&mut self, mut state_machine: StateMachineOwned<T>) {
84        match self {
85            StateMachine::Ready(_) => panic!(),
86            StateMachine::InUse { next_state, .. } => {
87                if let Some(next_state) = next_state {
88                    state_machine.set_state(*next_state);
89                }
90            }
91        }
92        *self = StateMachine::Ready(state_machine);
93    }
94
95    pub fn set_state(&mut self, state: usize) {
96        match self {
97            StateMachine::Ready(state_machine) => {
98                state_machine.set_state(state);
99            }
100            StateMachine::InUse {
101                ref mut next_state, ..
102            } => {
103                *next_state = Some(state);
104            }
105        }
106    }
107
108    pub const fn state(&self) -> usize {
109        match self {
110            StateMachine::Ready(state_machine) => state_machine.state(),
111            StateMachine::InUse {
112                ref current_state, ..
113            } => *current_state,
114        }
115    }
116
117    /// A hack to update a state machine being part of an updating struct
118    pub fn update_detached<'a, F>(mut t: scene::RefMut<T>, mut f: F)
119    where
120        F: FnMut(&mut scene::RefMut<T>) -> &mut StateMachine<T>,
121    {
122        let mut state_machine = f(&mut t).take();
123        state_machine.update(&mut t, crate::time::get_frame_time());
124
125        // coroutine may want to access this node by its handle
126        // but while we have borrowed t to RefMut - it will fail this attempt
127        // so here we drop t, poll the coroutine and than getting t back by its handle
128        let handle = t.handle();
129        drop(t);
130        if let Some(ref mut coroutine) = state_machine.active_coroutine {
131            coroutine.poll(crate::time::get_frame_time() as _);
132        }
133        let mut t = crate::experimental::scene::get_node(handle);
134
135        f(&mut t).put_back(state_machine);
136    }
137
138    pub fn update<'a>(&mut self, t: &'a mut scene::RefMut<T>) {
139        match self {
140            StateMachine::Ready(state_machine) => {
141                state_machine.update(t, crate::time::get_frame_time())
142            }
143            _ => panic!(),
144        }
145    }
146}
147
148pub struct StateMachineOwned<T: 'static> {
149    states: Vec<State<T>>,
150    active_coroutine: Option<Coroutine>,
151    next_state: Option<usize>,
152    current_state: usize,
153}
154
155impl<T: 'static> StateMachineOwned<T> {
156    const MAX_STATE: usize = 32;
157
158    pub fn new() -> Self {
159        let mut states = vec![];
160        for _ in 0..Self::MAX_STATE {
161            states.push(State::new());
162        }
163        StateMachineOwned {
164            states,
165            active_coroutine: None,
166            next_state: None,
167            current_state: 0,
168        }
169    }
170
171    pub fn insert(&mut self, id: usize, state: State<T>) {
172        assert!(id < Self::MAX_STATE);
173
174        self.states[id] = state;
175    }
176
177    pub fn set_state(&mut self, state: usize) {
178        self.next_state = Some(state);
179    }
180
181    pub const fn state(&self) -> usize {
182        self.current_state
183    }
184
185    fn update(&mut self, player: &mut scene::RefMut<T>, dt: f32) {
186        if let Some(next_state) = self.next_state {
187            if next_state != self.current_state {
188                if let Some(on_end) = &mut self.states[self.current_state].on_end {
189                    on_end(player);
190                }
191                if let Some(coroutine) = &mut self.states[next_state].coroutine {
192                    let mut coroutine = coroutine(player);
193                    coroutine.set_manual_poll();
194                    self.active_coroutine = Some(coroutine);
195                }
196            }
197            self.current_state = next_state;
198            self.next_state = None;
199        }
200
201        if let Some(update) = self.states[self.current_state].update.as_mut() {
202            (update)(player, dt);
203        }
204    }
205}