good_web_game/goodies/
scene.rs

1//! The Scene system is basically for transitioning between
2//! *completely* different states that have entirely different game
3//! loops and but which all share a state.  It operates as a stack, with new
4//! scenes getting pushed to the stack (while the old ones stay in
5//! memory unchanged).  Apparently this is basically a push-down automata.
6//!
7//! Also there's no reason you can't have a Scene contain its own
8//! Scene subsystem to do its own indirection.  With a different state
9//! type, as well!  What fun!  Though whether you want to go that deep
10//! down the rabbit-hole is up to you.  I haven't found it necessary
11//! yet.
12//!
13//! This is basically identical in concept to the Amethyst engine's scene
14//! system, the only difference is the details of how the pieces are put
15//! together.
16
17/// A command to change to a new scene, either by pushign a new one,
18/// popping one or replacing the current scene (pop and then push).
19pub enum SceneSwitch<C> {
20    None,
21    Push(Box<dyn Scene<C>>),
22    Replace(Box<dyn Scene<C>>),
23    Pop,
24}
25
26/// A trait for you to implement on a scene.
27/// Defines the callbacks the scene uses:
28/// a common context type `C`
29pub trait Scene<C> {
30    fn update(
31        &mut self,
32        gameworld: &mut C,
33        ctx: &mut crate::Context,
34        quad_ctx: &mut miniquad::graphics::GraphicsContext,
35    ) -> SceneSwitch<C>;
36    fn draw(
37        &mut self,
38        gameworld: &mut C,
39        ctx: &mut crate::Context,
40        quad_ctx: &mut miniquad::graphics::GraphicsContext,
41    ) -> crate::GameResult<()>;
42    fn resize_event(
43        &mut self,
44        _gameworld: &mut C,
45        _ctx: &mut crate::Context,
46        _quad_ctx: &mut miniquad::graphics::GraphicsContext,
47        _width: f32,
48        _height: f32,
49    ) {
50    }
51    #[allow(clippy::too_many_arguments)]
52    fn mouse_motion_event(
53        &mut self,
54        _gameworld: &mut C,
55        _ctx: &mut crate::Context,
56        _quad_ctx: &mut miniquad::graphics::GraphicsContext,
57        _x: f32,
58        _y: f32,
59        _dx: f32,
60        _dy: f32,
61    ) {
62    }
63    fn mouse_wheel_event(
64        &mut self,
65        _gameworld: &mut C,
66        _ctx: &mut crate::Context,
67        _quad_ctx: &mut miniquad::graphics::GraphicsContext,
68        _x: f32,
69        _y: f32,
70    ) {
71    }
72    fn mouse_button_down_event(
73        &mut self,
74        _gameworld: &mut C,
75        _ctx: &mut crate::Context,
76        _quad_ctx: &mut miniquad::graphics::GraphicsContext,
77        _button: crate::event::MouseButton,
78        _x: f32,
79        _y: f32,
80    ) {
81    }
82    fn mouse_button_up_event(
83        &mut self,
84        _gameworld: &mut C,
85        _ctx: &mut crate::Context,
86        _quad_ctx: &mut miniquad::graphics::GraphicsContext,
87        _button: crate::event::MouseButton,
88        _x: f32,
89        _y: f32,
90    ) {
91    }
92    fn key_down_event(
93        &mut self,
94        _gameworld: &mut C,
95        _ctx: &mut crate::Context,
96        _quad_ctx: &mut miniquad::graphics::GraphicsContext,
97        _key: crate::event::KeyCode,
98    ) {
99    }
100    fn key_up_event(
101        &mut self,
102        _gameworld: &mut C,
103        _ctx: &mut crate::Context,
104        _quad_ctx: &mut miniquad::graphics::GraphicsContext,
105        _key: crate::event::KeyCode,
106    ) {
107    }
108
109    /// Only used for human-readable convenience (or not at all, tbh)
110    fn name(&self) -> &str;
111    /// This returns whether or not to draw the next scene down on the
112    /// stack as well; this is useful for layers or GUI stuff that
113    /// only partially covers the screen.
114    fn draw_previous(&self) -> bool {
115        false
116    }
117}
118
119impl<C> SceneSwitch<C> {
120    /// Convenient shortcut function for boxing scenes.
121    ///
122    /// Slightly nicer than writing
123    /// `SceneSwitch::Replace(Box::new(x))` all the damn time.
124    pub fn replace<S>(scene: S) -> Self
125    where
126        S: Scene<C> + 'static,
127    {
128        SceneSwitch::Replace(Box::new(scene))
129    }
130
131    /// Same as `replace()` but returns SceneSwitch::Push
132    pub fn push<S>(scene: S) -> Self
133    where
134        S: Scene<C> + 'static,
135    {
136        SceneSwitch::Push(Box::new(scene))
137    }
138}
139
140/// A stack of `Scene`'s, together with a context object.
141pub struct SceneStack<C> {
142    pub world: C,
143    scenes: Vec<Box<dyn Scene<C>>>,
144}
145
146impl<C> SceneStack<C> {
147    pub fn new(_ctx: &mut crate::Context, global_state: C) -> Self {
148        Self {
149            world: global_state,
150            scenes: Vec::new(),
151        }
152    }
153
154    /// Add a new scene to the top of the stack.
155    pub fn push(&mut self, scene: Box<dyn Scene<C>>) {
156        self.scenes.push(scene)
157    }
158
159    /// Remove the top scene from the stack and returns it;
160    /// panics if there is none.
161    pub fn pop(&mut self) -> Box<dyn Scene<C>> {
162        self.scenes
163            .pop()
164            .expect("ERROR: Popped an empty scene stack.")
165    }
166
167    /// Returns the current scene; panics if there is none.
168    pub fn current(&self) -> &dyn Scene<C> {
169        &**self
170            .scenes
171            .last()
172            .expect("ERROR: Tried to get current scene of an empty scene stack.")
173    }
174
175    /// Executes the given SceneSwitch command; if it is a pop or replace
176    /// it returns `Some(old_scene)`, otherwise `None`
177    pub fn switch(&mut self, next_scene: SceneSwitch<C>) -> Option<Box<dyn Scene<C>>> {
178        match next_scene {
179            SceneSwitch::None => None,
180            SceneSwitch::Pop => {
181                let s = self.pop();
182                Some(s)
183            }
184            SceneSwitch::Push(s) => {
185                self.push(s);
186                None
187            }
188            SceneSwitch::Replace(s) => {
189                let old_scene = self.pop();
190                self.push(s);
191                Some(old_scene)
192            }
193        }
194    }
195
196    // These functions must be on the SceneStack because otherwise
197    // if you try to get the current scene and the world to call
198    // update() on the current scene it causes a double-borrow.  :/
199    pub fn update(
200        &mut self,
201        ctx: &mut crate::Context,
202        quad_ctx: &mut miniquad::graphics::GraphicsContext,
203    ) {
204        let next_scene = {
205            let current_scene = &mut **self
206                .scenes
207                .last_mut()
208                .expect("Tried to update empty scene stack");
209            current_scene.update(&mut self.world, ctx, quad_ctx)
210        };
211        self.switch(next_scene);
212    }
213
214    /// We walk down the scene stack until we find a scene where we aren't
215    /// supposed to draw the previous one, then draw them from the bottom up.
216    ///
217    /// This allows for layering GUI's and such.
218    fn draw_scenes(
219        scenes: &mut [Box<dyn Scene<C>>],
220        world: &mut C,
221        ctx: &mut crate::Context,
222        quad_ctx: &mut miniquad::graphics::GraphicsContext,
223    ) {
224        assert!(!scenes.is_empty());
225        if let Some((current, rest)) = scenes.split_last_mut() {
226            if current.draw_previous() {
227                SceneStack::draw_scenes(rest, world, ctx, quad_ctx);
228            }
229            current
230                .draw(world, ctx, quad_ctx)
231                .expect("I would hope drawing a scene never fails!");
232        }
233    }
234
235    /// Draw the current scene.
236    pub fn draw(
237        &mut self,
238        ctx: &mut crate::Context,
239        quad_ctx: &mut miniquad::graphics::GraphicsContext,
240    ) {
241        SceneStack::draw_scenes(&mut self.scenes, &mut self.world, ctx, quad_ctx)
242    }
243}
244
245impl<C, E: std::error::Error> crate::event::EventHandler<E> for SceneStack<C> {
246    fn update(
247        &mut self,
248        ctx: &mut crate::Context,
249        quad_ctx: &mut miniquad::graphics::GraphicsContext,
250    ) -> Result<(), E> {
251        self.update(ctx, quad_ctx);
252        Ok(())
253    }
254
255    fn draw(
256        &mut self,
257        ctx: &mut crate::Context,
258        quad_ctx: &mut miniquad::graphics::GraphicsContext,
259    ) -> Result<(), E> {
260        self.draw(ctx, quad_ctx);
261        Ok(())
262    }
263
264    fn resize_event(
265        &mut self,
266        ctx: &mut crate::Context,
267        quad_ctx: &mut miniquad::graphics::GraphicsContext,
268        width: f32,
269        height: f32,
270    ) {
271        let current_scene = &mut **self
272            .scenes
273            .last_mut()
274            .expect("Tried to update empty scene stack");
275
276        current_scene.resize_event(&mut self.world, ctx, quad_ctx, width, height)
277    }
278
279    fn mouse_motion_event(
280        &mut self,
281        ctx: &mut crate::Context,
282        quad_ctx: &mut miniquad::graphics::GraphicsContext,
283        x: f32,
284        y: f32,
285        dx: f32,
286        dy: f32,
287    ) {
288        let current_scene = &mut **self
289            .scenes
290            .last_mut()
291            .expect("Tried to update empty scene stack");
292
293        current_scene.mouse_motion_event(&mut self.world, ctx, quad_ctx, x, y, dx, dy);
294    }
295
296    fn mouse_wheel_event(
297        &mut self,
298        ctx: &mut crate::Context,
299        quad_ctx: &mut miniquad::graphics::GraphicsContext,
300        x: f32,
301        y: f32,
302    ) {
303        let current_scene = &mut **self
304            .scenes
305            .last_mut()
306            .expect("Tried to update empty scene stack");
307
308        current_scene.mouse_wheel_event(&mut self.world, ctx, quad_ctx, x, y);
309    }
310
311    fn mouse_button_down_event(
312        &mut self,
313        ctx: &mut crate::Context,
314        quad_ctx: &mut miniquad::graphics::GraphicsContext,
315        button: crate::event::MouseButton,
316        x: f32,
317        y: f32,
318    ) {
319        let current_scene = &mut **self
320            .scenes
321            .last_mut()
322            .expect("Tried to update empty scene stack");
323
324        current_scene.mouse_button_down_event(&mut self.world, ctx, quad_ctx, button, x, y);
325    }
326    fn mouse_button_up_event(
327        &mut self,
328        ctx: &mut crate::Context,
329        quad_ctx: &mut miniquad::graphics::GraphicsContext,
330        button: crate::event::MouseButton,
331        x: f32,
332        y: f32,
333    ) {
334        let current_scene = &mut **self
335            .scenes
336            .last_mut()
337            .expect("Tried to update empty scene stack");
338
339        current_scene.mouse_button_up_event(&mut self.world, ctx, quad_ctx, button, x, y);
340    }
341
342    fn key_down_event(
343        &mut self,
344        ctx: &mut crate::Context,
345        quad_ctx: &mut miniquad::graphics::GraphicsContext,
346        keycode: crate::event::KeyCode,
347        _keymods: crate::event::KeyMods,
348        _repeat: bool,
349    ) {
350        let current_scene = &mut **self
351            .scenes
352            .last_mut()
353            .expect("Tried to update empty scene stack");
354
355        current_scene.key_down_event(&mut self.world, ctx, quad_ctx, keycode);
356    }
357
358    fn key_up_event(
359        &mut self,
360        ctx: &mut crate::Context,
361        quad_ctx: &mut miniquad::graphics::GraphicsContext,
362        keycode: crate::event::KeyCode,
363        _keymods: crate::event::KeyMods,
364    ) {
365        let current_scene = &mut **self
366            .scenes
367            .last_mut()
368            .expect("Tried to update empty scene stack");
369
370        current_scene.key_up_event(&mut self.world, ctx, quad_ctx, keycode);
371    }
372}