diff --git a/assets/img/logos/parry.png b/assets/img/logos/parry.png new file mode 100644 index 0000000..7046d2b Binary files /dev/null and b/assets/img/logos/parry.png differ diff --git a/assets/img/logos/raylib-rs.png b/assets/img/logos/raylib-rs.png new file mode 100644 index 0000000..c7ad463 Binary files /dev/null and b/assets/img/logos/raylib-rs.png differ diff --git a/assets/img/logos/rust.png b/assets/img/logos/rust.png new file mode 100644 index 0000000..a706d7c Binary files /dev/null and b/assets/img/logos/rust.png differ diff --git a/assets/img/logos/serde.png b/assets/img/logos/serde.png new file mode 100644 index 0000000..3c0e5a7 Binary files /dev/null and b/assets/img/logos/serde.png differ diff --git a/src/gamecore.rs b/src/gamecore.rs index 25526e3..91501a6 100644 --- a/src/gamecore.rs +++ b/src/gamecore.rs @@ -5,7 +5,7 @@ use std::fmt; use crate::resources::GlobalResources; /// Overall states for the game -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum GameState { Loading, MainMenu, @@ -22,6 +22,7 @@ pub struct GameCore { /// The game's overall state pub state: GameState, pub last_state_change_time: f64, + pub has_rendered_first_frame: bool, /// Resources pub resources: Option, @@ -32,6 +33,7 @@ impl GameCore { Self { state: GameState::Loading, last_state_change_time: 0.0, + has_rendered_first_frame:false, resources: None, } } diff --git a/src/logic/loadingscreen.rs b/src/logic/loadingscreen.rs index 05520c4..afb4e02 100644 --- a/src/logic/loadingscreen.rs +++ b/src/logic/loadingscreen.rs @@ -1,13 +1,222 @@ use raylib::prelude::*; -use crate::gamecore::{GameCore, GameState}; +use crate::{ + gamecore::{GameCore, GameState}, + lib::wrappers::audio::player::AudioPlayer, + resources::GlobalResources, +}; +use super::screen::Screen; -pub fn handle_loading_screen(draw_handle: &mut RaylibDrawHandle, game_core: &mut GameCore) -> Option { +const SECONDS_PER_LOGO: f64 = 2.0; +const RUST_ORANGE: Color = Color::new(222, 165, 132, 255); - // Clear frame - draw_handle.clear_background(Color::WHITE); +#[derive(Debug, PartialEq)] +enum LoadingScreenState { + Preload, + LoadingResources, + GameLogo, + RaylibLogo, + Finished, +} +pub struct LoadingScreen { + state: LoadingScreenState, + last_state_switch_time: f64, +} - return None; -} \ No newline at end of file +impl LoadingScreen { + pub fn new() -> Self { + Self { + state: LoadingScreenState::Preload, + last_state_switch_time: 0.0, + } + } + + fn load_global_resources( + &mut self, + draw_handle: &mut RaylibDrawHandle, + game_core: &mut GameCore, + win_height: i32, + win_width: i32, + ) { + // Show a loading message (this will stay on screen until all resources are loaded) + draw_handle.draw_text( + "Loading Assets...", + (win_width / 2) - 90, + (win_height / 3) * 2, + 25, + Color::BLACK, + ); + + if self.state == LoadingScreenState::LoadingResources { + // Load the global resources + let resources = GlobalResources::load_all(); + + // Handle a loading error + if resources.is_err() { + println!("ERROR: Failed to load game resources!"); + panic!("{:?}", resources.err()); + } + + // Set the global resources + game_core.resources = Some(resources.unwrap()); + + // Set the loading screen state to move on to the game logo + self.state = LoadingScreenState::GameLogo; + self.last_state_switch_time = draw_handle.get_time(); + return; + } + + // Update internal state + self.state = LoadingScreenState::LoadingResources; + } + + fn get_logo_mask(&self, draw_handle: &RaylibDrawHandle, playthrough_percent: f64) -> Color { + // Determine the alpha + let alpha; + if playthrough_percent < 0.25 { + alpha = playthrough_percent / 0.25 + } else if playthrough_percent > 0.75 { + alpha = 1.0 - ((playthrough_percent - 0.75) / 0.25); + } else { + alpha = 1.0; + } + + // Build a color mask + Color { + r: 255, + g: 255, + b: 255, + a: (255.0 * alpha) as u8, + } + } + + fn show_game_logo( + &mut self, + draw_handle: &mut RaylibDrawHandle, + game_core: &mut GameCore, + win_height: i32, + win_width: i32, + ) { + // Determine how far through rendering this logo we are + // This value is used to determine the logo alpha + let playthrough_percent = + (draw_handle.get_time() - self.last_state_switch_time) / SECONDS_PER_LOGO; + + // Build a color mask + let mask = self.get_logo_mask(draw_handle, playthrough_percent); + + // Render the logo + // TODO + + // Move on to next logo if needed + if playthrough_percent >= 1.0 { + self.state = LoadingScreenState::RaylibLogo; + self.last_state_switch_time = draw_handle.get_time(); + } + } + + fn show_raylib_logo( + &mut self, + draw_handle: &mut RaylibDrawHandle, + game_core: &mut GameCore, + win_height: i32, + win_width: i32, + ) { + // Determine how far through rendering this logo we are + // This value is used to determine the logo alpha + let playthrough_percent = + (draw_handle.get_time() - self.last_state_switch_time) / SECONDS_PER_LOGO; + + // Build a color mask + let mask = self.get_logo_mask(draw_handle, playthrough_percent); + + // Create modified colors + let alpha_orange = Color { + r: RUST_ORANGE.r, + g: RUST_ORANGE.g, + b: RUST_ORANGE.b, + a: mask.a + }; + let alpha_black = Color { + r: Color::BLACK.r, + g: Color::BLACK.g, + b: Color::BLACK.b, + a: mask.a + }; + + // Render the raylib logo + draw_handle.draw_rectangle( + win_width / 2 - 128, + win_height / 2 - 128, + 256, + 256, + alpha_orange, + ); + draw_handle.draw_rectangle( + win_width / 2 - 112, + win_height / 2 - 112, + 224, + 224, + Color::WHITE, + ); + draw_handle.draw_text( + "rust", + win_width / 2 - 69, + win_height / 2 + 18, + 50, + alpha_orange, + ); + draw_handle.draw_text( + "raylib", + win_width / 2 - 44, + win_height / 2 + 48, + 50, + alpha_orange, + ); + + // Move on to next logo if needed + if playthrough_percent >= 1.0 { + self.state = LoadingScreenState::Finished; + self.last_state_switch_time = draw_handle.get_time(); + } + } +} + +impl Screen for LoadingScreen { + fn render( + &mut self, + draw_handle: &mut RaylibDrawHandle, + _audio_system: &mut AudioPlayer, + game_core: &mut GameCore, + ) -> Option { + // Clear frame + draw_handle.clear_background(Color::WHITE); + + // Window dimensions + let win_height = draw_handle.get_screen_height(); + let win_width = draw_handle.get_screen_width(); + + //TODO: Debug mode skip button + + // Call the appropriate internal handler function + match self.state { + LoadingScreenState::Preload => { + self.load_global_resources(draw_handle, game_core, win_height, win_width) + } + LoadingScreenState::LoadingResources => { + self.load_global_resources(draw_handle, game_core, win_height, win_width) + } + LoadingScreenState::GameLogo => { + self.show_game_logo(draw_handle, game_core, win_height, win_width) + } + LoadingScreenState::RaylibLogo => { + self.show_raylib_logo(draw_handle, game_core, win_height, win_width) + } + LoadingScreenState::Finished => return Some(GameState::MainMenu), + } + + return None; + } +} diff --git a/src/logic/mainmenu.rs b/src/logic/mainmenu.rs index c393a13..ce5cc62 100644 --- a/src/logic/mainmenu.rs +++ b/src/logic/mainmenu.rs @@ -1,11 +1,24 @@ use raylib::prelude::*; -use crate::gamecore::{GameCore, GameState}; +use crate::{gamecore::{GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer}; +use super::screen::Screen; -pub fn handle_main_menu(draw_handle: &mut RaylibDrawHandle, game_core: &mut GameCore) -> Option{ - +pub struct MainMenuScreen {} - return None; +impl MainMenuScreen { + pub fn new() -> Self { + Self {} + } +} -} \ No newline at end of file +impl Screen for MainMenuScreen { + fn render( + &mut self, + draw_handle: &mut RaylibDrawHandle, + audio_system: &mut AudioPlayer, + game_core: &mut GameCore, + ) -> Option { + return None; + } +} diff --git a/src/logic/mod.rs b/src/logic/mod.rs index e4105ac..8ea3fe4 100644 --- a/src/logic/mod.rs +++ b/src/logic/mod.rs @@ -1,2 +1,4 @@ +pub mod screen; pub mod loadingscreen; -pub mod mainmenu; \ No newline at end of file +pub mod mainmenu; + diff --git a/src/logic/screen.rs b/src/logic/screen.rs new file mode 100644 index 0000000..e89d603 --- /dev/null +++ b/src/logic/screen.rs @@ -0,0 +1,13 @@ +use raylib::prelude::RaylibDrawHandle; + +use crate::{gamecore::{GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer}; + +/// A trait describing all game screens +pub trait Screen { + fn render( + &mut self, + draw_handle: &mut RaylibDrawHandle, + audio_system: &mut AudioPlayer, + game_core: &mut GameCore, + ) -> Option; +} diff --git a/src/main.rs b/src/main.rs index 984e328..02fc2fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,14 @@ mod resources; use gamecore::{GameCore, GameState}; use lib::{utils::profiler::GameProfiler, wrappers::audio::player::AudioPlayer}; -use logic::{loadingscreen::handle_loading_screen, mainmenu::handle_main_menu}; +use logic::{loadingscreen::LoadingScreen, mainmenu::MainMenuScreen, screen::Screen}; use raylib::prelude::*; // Game Launch Configuration -const DEFAULT_WINDOW_DIMENSIONS: Vector2 = Vector2 { x: 800.0, y: 600.0 }; +const DEFAULT_WINDOW_DIMENSIONS: Vector2 = Vector2 { + x: 1080.0, + y: 720.0, +}; const WINDOW_TITLE: &str = r"Ludum Dare 48"; const MAX_FPS: u32 = 60; @@ -37,14 +40,22 @@ fn main() { // Init the audio subsystem let mut audio_system = AudioPlayer::new(RaylibAudio::init_audio_device()); + // Create all the game screens + let mut loading_screen = LoadingScreen::new(); + let mut main_menu_screen = MainMenuScreen::new(); + // Main rendering loop while !raylib.window_should_close() { let mut draw_handle = raylib.begin_drawing(&raylib_thread); // Call appropriate render function let new_state: Option = match game_core.state { - GameState::Loading => handle_loading_screen(&mut draw_handle, &mut game_core), - GameState::MainMenu => handle_main_menu(&mut draw_handle, &mut game_core), + GameState::Loading => { + loading_screen.render(&mut draw_handle, &mut audio_system, &mut game_core) + } + GameState::MainMenu => { + main_menu_screen.render(&mut draw_handle, &mut audio_system, &mut game_core) + } }; if new_state.is_some() { @@ -67,6 +78,9 @@ fn main() { // Send telemetry data profiler.update(); } + + // Set the first frame flag + game_core.has_rendered_first_frame = true; } // Cleanup diff --git a/src/resources.rs b/src/resources.rs index 9133592..16e485b 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -5,7 +5,7 @@ pub struct GlobalResources {} impl GlobalResources { /// Load all resources. **THIS WILL HANG!** - pub fn load_all(&mut self) -> Result { + pub fn load_all() -> Result { Ok(GlobalResources {}) } }