bracket_state_machine/
state.rs1use bracket_lib::prelude::*;
2use std::time::Duration;
3
4pub type StateTransition<S, R> = Transition<S, R>;
5type UpdateReturn<S, R> = (StateTransition<S, R>, TransitionControl);
6
7pub trait State {
8 type State: ?Sized;
9 type StateResult;
10
11 #[must_use = "it may trigger a state change"]
12 fn update(
13 &mut self,
14 term: &mut BTerm,
15 state: &mut Self::State,
16 pop_result: &Option<Self::StateResult>,
17 dt: Duration,
18 ) -> UpdateReturn<Self::State, Self::StateResult>
19 where
20 Self::State: std::marker::Sized,
21 Self::StateResult: std::marker::Sized;
22
23 fn render(&self, term: &mut BTerm, state: &Self::State, active: bool);
24
25 fn clear(&self, _state: &Self::State, _term: &mut BTerm) {
26 BACKEND_INTERNAL
27 .lock()
28 .consoles
29 .iter_mut()
30 .for_each(|c| c.console.cls());
31 }
32
33 fn is_transparent(&self) -> bool {
34 true
35 }
36}
37
38pub enum RunControl {
40 Quit,
42 Update,
44 WaitForEvent,
47}
48
49pub enum Transition<S, R> {
50 Stay,
51 Pop(R),
52 Terminate,
53 Push(Box<dyn State<State = S, StateResult = R>>),
54 Switch(Box<dyn State<State = S, StateResult = R>>),
55}
56
57#[derive(Debug)]
59pub enum TransitionControl {
60 Immediate,
62 Update,
64 WaitForEvent,
67}
68
69pub struct StateMachine<S, R> {
70 state: S,
71 wait_for_event: bool,
72 pop_result: Option<R>,
73 active_mouse_pos: Point,
74 states: Vec<Box<dyn State<State = S, StateResult = R>>>,
75}
76
77impl<S, R> StateMachine<S, R> {
78 pub fn new<T: State<State = S, StateResult = R> + 'static>(
81 system_state: S,
82 init_state: T,
83 ) -> Self {
84 StateMachine {
85 pop_result: None,
86 state: system_state,
87 wait_for_event: false,
88 active_mouse_pos: Point::zero(),
89 states: vec![Box::new(init_state)],
90 }
91 }
92
93 fn clear_consoles(&mut self, term: &mut BTerm) {
94 if let Some(top_state) = self.states.last_mut() {
95 top_state.clear(&self.state, term);
96 }
97 }
98
99 fn internal_tick(&mut self, ctx: &mut BTerm) -> RunControl {
100 while !self.states.is_empty() {
101 let (transition, transition_update) = {
102 let top_mode = self.states.last_mut().unwrap();
103 top_mode.update(
104 ctx,
105 &mut self.state,
106 &self.pop_result,
107 Duration::from_millis(ctx.frame_time_ms as u64),
108 )
109 };
110
111 self.pop_result = None;
112 match transition {
113 Transition::Stay => {}
114 Transition::Switch(state) => {
115 self.states.pop();
116 self.states.push(state);
117 }
118 Transition::Push(state) => {
119 self.states.push(state);
120 }
121 Transition::Pop(state_result) => {
122 self.pop_result = Some(state_result);
123 self.states.pop();
124 }
125 Transition::Terminate => {
126 self.states.clear();
127 }
128 }
129
130 if !self.states.is_empty() && !matches!(transition_update, TransitionControl::Immediate)
132 {
133 let draw_from = self
134 .states
135 .iter()
136 .rposition(|mode| !mode.is_transparent())
137 .unwrap_or(0);
138
139 let top = self.states.len().saturating_sub(1);
140
141 self.clear_consoles(ctx);
142
143 for mode in self.states.iter_mut().skip(usize::max(draw_from, 1)) {
145 mode.render(ctx, &self.state, false);
146 }
147
148 self.states[top].render(ctx, &self.state, true);
150
151 render_draw_buffer(ctx).expect("Render draw buffer error");
152 }
153
154 match transition_update {
155 TransitionControl::Immediate => (),
156 TransitionControl::Update => return RunControl::Update,
157 TransitionControl::WaitForEvent => return RunControl::WaitForEvent,
158 }
159 }
160
161 RunControl::Quit
162 }
163}
164
165impl<S: 'static, R: 'static> GameState for StateMachine<S, R> {
166 fn tick(&mut self, ctx: &mut BTerm) {
167 if ctx.quitting {
168 ctx.quit();
169 }
170
171 if self.wait_for_event {
172 let new_mouse = ctx.mouse_point();
173
174 if ctx.key.is_some() || ctx.left_click {
176 self.wait_for_event = false;
177 }
178
179 if new_mouse != self.active_mouse_pos {
181 self.wait_for_event = false;
182 self.active_mouse_pos = new_mouse;
183 }
184 } else {
185 self.active_mouse_pos = ctx.mouse_point();
186
187 match self.internal_tick(ctx) {
188 RunControl::Update => {}
189 RunControl::Quit => ctx.quit(),
190 RunControl::WaitForEvent => self.wait_for_event = true,
191 }
192 }
193 }
194}