//! 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), /// 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; 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(); } } } } }