//! 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; #[repr(u8)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Key { // Movement W, A, S, D, // Movement (Arrow) Up, Down, Left, Right, // Debug keys F3, // 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, // Debug keys K::KEY_F3 => Self::F3, // 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, // Debug keys Key::F3 => Self::KEY_F3, // 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 fn new() -> Self { Self::default() } /// Set the window title pub fn title(&mut self, title: &'a str) -> &mut Self { self.title = title; self } /// Set the default window width pub fn width(&mut self, width: u16) -> &mut Self { self.width = width; self } /// Set the default window height pub fn height(&mut self, height: u16) -> &mut Self { self.height = height; self } /// Toggle vsync support pub fn vsync(&mut self, vsync: bool) -> &mut Self { self.vsync = vsync; self } /// Toggle verbose logging pub 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); } // Set highdpi for wayland (this is only an issue for glfw) // Rust binding to not make this accessable #[cfg(feature = "wayland")] #[cfg(not(feature = "sdl"))] unsafe { use ffi::ConfigFlags::*; ffi::SetConfigFlags(FLAG_WINDOW_HIGHDPI as u32); } 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 { title: "Dungeon Crawl", width: render::RENDER_WIDTH, height: render::RENDER_HEIGHT, vsync: false, verbose: false, } } } /// 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 fn toggle_debug(&mut self) { self.renderer.toggle_debug(); } /// Returns the per frame delta time pub 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 fn audio(&self) -> &Audio { &self.audio } }