summaryrefslogtreecommitdiff
path: root/src/scenes/mod.rs
blob: 60db6477d34ebacb310338dc36a66d187bbbd536 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//! Scenes: the title screen, the name forge, and derelicts to board;
//! flight and ports later.

pub mod derelict;
pub mod forge;
pub mod title;

use ratatui::Frame;
use ratatui::crossterm::event::KeyEvent;

use crate::app::Ctx;

/// What a scene wants to happen after it handles a key or a tick.
pub enum Transition {
    None,
    /// Open a scene on top of this one. Popping it comes back here.
    Push(Box<dyn Scene>),
    /// Close this scene and return to the one beneath it.
    Pop,
    Quit,
}

/// One screen of the game. The game keeps a stack of them; only the top one
/// gets keys and ticks and is drawn.
pub trait Scene {
    /// A key was pressed, or auto-repeated while held.
    fn key(&mut self, key: KeyEvent, ctx: &mut Ctx) -> Transition;

    /// One fixed step of game time; there are `app::TICKS_PER_SECOND` of
    /// these a second, whatever the frame rate. Turn-based scenes can ignore it.
    fn tick(&mut self, _ctx: &mut Ctx) -> Transition {
        Transition::None
    }

    fn render(&self, frame: &mut Frame, ctx: &Ctx);
}

#[cfg(test)]
mod tests {
    use ratatui::Terminal;
    use ratatui::backend::TestBackend;
    use ratatui::crossterm::event::{KeyCode, KeyModifiers};

    use super::*;
    use crate::term::KeyboardCaps;

    pub fn test_ctx() -> Ctx {
        Ctx::new(1, KeyboardCaps { key_release: false })
    }

    /// Draws every scene at several sizes, down to absurdly small ones, to
    /// catch layout arithmetic that panics.
    #[test]
    fn scenes_render_at_any_size() {
        let mut ctx = test_ctx();
        let mut abandoning = derelict::Derelict::new(2);
        abandoning.key(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE), &mut ctx);
        let scenes: [Box<dyn Scene>; 4] = [
            Box::new(title::Title::new(&ctx)),
            Box::new(forge::Forge::new(&ctx)),
            Box::new(derelict::Derelict::new(1)),
            Box::new(abandoning),
        ];
        for (width, height) in [(160, 50), (80, 24), (40, 12), (5, 3), (1, 1)] {
            let mut terminal = Terminal::new(TestBackend::new(width, height)).unwrap();
            for scene in &scenes {
                for ticks in [0, 12_345] {
                    ctx.ticks = ticks;
                    terminal.draw(|frame| scene.render(frame, &ctx)).unwrap();
                }
            }
        }
    }
}