//! The `graphics` crate contains the core functionality for //! rendering using the `raylib` library. use std::borrow::Cow; use std::cell::RefCell; use dungeon::Dungeon; use raylib::prelude::*; use crate::audio::Audio; use crate::render::Renderer; mod audio; mod render; /// The `KeyCode` type represents different keys being pressed on the users keyboard pub use raylib::consts::KeyboardKey as KeyCode; /// The `Error` type used within this crate pub type Error = Box; /// The `Result` type used witin this crate pub type Result = std::result::Result; /// The `WindowBuilder` type allows setting partial options for the window #[derive(Debug)] pub struct WindowBuilder<'a> { title: Cow<'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: impl Into>) -> &mut Self { self.title = title.into(); 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 { handle: RefCell::new(handle), thread, renderer, audio, }) } } impl Default for WindowBuilder<'_> { fn default() -> Self { Self { title: Cow::Borrowed("Dungeon Crawl"), width: render::RENDER_WIDTH, height: render::RENDER_HEIGHT, vsync: false, verbose: false, } } } /// The `Window` type represents the game window #[derive(Debug)] pub struct Window { // persistant renderer renderer: Renderer, // core raylib handles handle: RefCell, 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.borrow().window_should_close() } /// Draws the next ingame frame pub fn draw_frame(&mut self, dungeon: &Dungeon) { self.renderer .draw_frame(self.handle.get_mut(), &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) -> f32 { self.handle.borrow().get_frame_time() } /// Returns if the provided `KeyCode` has been pressed once pub fn is_key_pressed(&self, key: KeyCode) -> bool { self.handle.borrow().is_key_pressed(key) } /// Returns if the provided `KeyCode` is NOT currently pressed pub fn is_key_up(&self, key: KeyCode) -> bool { self.handle.borrow().is_key_up(key) } /// Returns if the provided `KeyCode` is currently pressed pub fn is_key_down(&self, key: KeyCode) -> bool { self.handle.borrow().is_key_down(key) } /// Get the last key pressed pub fn get_key_pressed(&self) -> Option { // We dont want to require a mutable reference // to read the last key pressed self.handle .try_borrow_mut() .ok() .and_then(|mut h| h.get_key_pressed()) } /// Get audio data for the window pub fn audio(&self) -> &Audio { &self.audio } }