//! The `graphics` crate contains the core functionality for //! rendering using the `raylib` library. use std::time::Duration; use dungeon::Dungeon; use raylib::prelude::*; use crate::audio::Audio; use crate::render::Renderer; mod audio; mod render; mod timer; /// The `Error` type used within this crate pub type Error = Box; /// The `Result` type used witin this crate pub type Result = std::result::Result; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Key { // Movement W, A, S, D, // Movement (Arrow) Up, Down, Left, Right, // Interact Return, E, F, Q, // Number Number(usize), // Debug keys F3, F4, // Unknown/Unused key Unknown(KeyboardKey), } impl From for Key { fn from(key: KeyboardKey) -> Self { use KeyboardKey as K; match key { // Movement K::KEY_W => Self::W, K::KEY_A => Self::A, K::KEY_S => Self::S, K::KEY_D => Self::D, // Movement (Arrow) K::KEY_UP => Self::Up, K::KEY_DOWN => Self::Down, K::KEY_LEFT => Self::Left, K::KEY_RIGHT => Self::Right, // Interact K::KEY_ENTER => Self::Return, K::KEY_E => Self::E, K::KEY_F => Self::F, K::KEY_Q => Self::Q, // Number K::KEY_ZERO | K::KEY_KP_0 => Self::Number(0), K::KEY_ONE | K::KEY_KP_1 => Self::Number(1), K::KEY_TWO | K::KEY_KP_2 => Self::Number(2), K::KEY_THREE | K::KEY_KP_3 => Self::Number(3), K::KEY_FOUR | K::KEY_KP_4 => Self::Number(4), K::KEY_FIVE | K::KEY_KP_5 => Self::Number(5), K::KEY_SIX | K::KEY_KP_6 => Self::Number(6), K::KEY_SEVEN | K::KEY_KP_7 => Self::Number(7), K::KEY_EIGHT | K::KEY_KP_8 => Self::Number(8), K::KEY_NINE | K::KEY_KP_9 => Self::Number(9), // Debug keys K::KEY_F3 => Self::F3, K::KEY_F4 => Self::F4, // Unknown/Unused key _ => Self::Unknown(key), } } } impl From for KeyboardKey { fn from(key: Key) -> Self { match key { // Movement Key::W => Self::KEY_W, Key::A => Self::KEY_A, Key::S => Self::KEY_S, Key::D => Self::KEY_D, // Movement (Arrow) Key::Up => Self::KEY_UP, Key::Down => Self::KEY_DOWN, Key::Left => Self::KEY_LEFT, Key::Right => Self::KEY_RIGHT, // Interact Key::Return => Self::KEY_ENTER, Key::E => Self::KEY_E, Key::F => Self::KEY_F, Key::Q => Self::KEY_Q, // Number Key::Number(0) => Self::KEY_ZERO, Key::Number(1) => Self::KEY_ONE, Key::Number(2) => Self::KEY_TWO, Key::Number(3) => Self::KEY_THREE, Key::Number(4) => Self::KEY_FOUR, Key::Number(5) => Self::KEY_FIVE, Key::Number(6) => Self::KEY_SIX, Key::Number(7) => Self::KEY_SEVEN, Key::Number(8) => Self::KEY_EIGHT, Key::Number(9) => Self::KEY_NINE, Key::Number(_) => Self::KEY_NULL, // Debug keys Key::F3 => Self::KEY_F3, Key::F4 => Self::KEY_F4, // Unknown/Unused key Key::Unknown(k) => k, } } } /// The `WindowBuilder` type allows setting partial options for the window pub struct WindowBuilder<'a> { title: &'a str, width: u16, height: u16, vsync: bool, verbose: bool, } impl<'a> WindowBuilder<'a> { /// Default window builder pub const fn new() -> Self { Self { title: "Dungeon Crawl", width: render::RENDER_WIDTH, height: render::RENDER_HEIGHT, vsync: false, verbose: false, } } /// Set the window title pub const fn title(&mut self, title: &'a str) -> &mut Self { self.title = title; self } /// Set the default window width pub const fn width(&mut self, width: u16) -> &mut Self { self.width = width; self } /// Set the default window height pub const fn height(&mut self, height: u16) -> &mut Self { self.height = height; self } /// Toggle vsync support pub const fn vsync(&mut self, vsync: bool) -> &mut Self { self.vsync = vsync; self } /// Toggle verbose logging pub const fn verbose(&mut self, verbose: bool) -> &mut Self { self.verbose = verbose; self } /// Build the window pub fn build(&self) -> crate::Result { let mut builder = raylib::init(); // Set raylib args from builder builder.size(self.width.into(), self.height.into()); builder.title(self.title); builder.resizable(); if self.vsync { builder.vsync(); } if self.verbose { builder.log_level(TraceLogLevel::LOG_INFO); } else { builder.log_level(TraceLogLevel::LOG_WARNING); } let (mut handle, thread) = builder.build(); if !handle.is_window_ready() { return Err("Raylib window not ready!".into()); } // update window min size handle.set_window_min_size(self.width.into(), self.height.into()); // load audio let audio = Audio::load()?; // load renderer let renderer = Renderer::new(&mut handle, &thread)?; Ok(Window { renderer, handle, thread, audio, }) } } impl Default for WindowBuilder<'_> { fn default() -> Self { Self::new() } } /// The `Window` type represents the game window pub struct Window { // persistant renderer renderer: Renderer, // core raylib handles handle: RaylibHandle, thread: RaylibThread, // audio data/subsystem audio: Audio, } impl Window { /// Returns if the window should be closed. /// This usually means the 'x' button has been pressed. pub fn is_open(&self) -> bool { !self.handle.window_should_close() } /// Draws the next ingame frame pub fn draw_frame(&mut self, dungeon: &Dungeon) { self.renderer .draw_frame(&mut self.handle, &self.thread, dungeon); } /// Toggles the debug UI pub const fn toggle_debug(&mut self) { self.renderer.toggle_debug(); } /// Returns the per frame delta time pub const fn delta_time(&self) -> Duration { self.renderer.delta_time() } /// Returns if the provided `Key` has just been pressed pub fn is_key_pressed(&self, key: Key) -> bool { self.handle.is_key_pressed(key.into()) } /// Returns if the provided `Key` has just been released pub fn is_key_released(&self, key: Key) -> bool { self.handle.is_key_released(key.into()) } /// Returns if the provided `Key` is NOT currently pressed pub fn is_key_up(&self, key: Key) -> bool { self.handle.is_key_up(key.into()) } /// Returns if the provided `Key` is currently pressed pub fn is_key_down(&self, key: Key) -> bool { self.handle.is_key_down(key.into()) } /// Get audio data for the window pub const fn audio(&self) -> &Audio { &self.audio } }