diff --git a/game/game_logic/src/scenes/cutscenes.rs b/game/game_logic/src/scenes/cutscenes.rs new file mode 100644 index 00000000..d5effa76 --- /dev/null +++ b/game/game_logic/src/scenes/cutscenes.rs @@ -0,0 +1,264 @@ +//! This scene encompasses the main menu system + +use chrono::Duration; +use na::Vector1; +use nalgebra as na; +use raylib::{ + ffi::{GetMouseX, GetMouseY, IsMouseButtonDown, Texture}, + prelude::*, +}; + +use crate::{ + discord::{DiscordChannel, DiscordRpcSignal}, + global_resource_package::GlobalResources, + persistent::settings::PersistentGameSettings, + project_constants::ProjectConstants, +}; + +use super::main_menu::MenuStateSignal; + +#[derive(Debug)] +pub struct CutScenes { + show_debug_info: bool, +} + +impl CutScenes { + /// Construct a new `CutScenes` + pub fn new( + raylib_handle: &mut RaylibHandle, + thread: &RaylibThread, + constants: &ProjectConstants, + game_settings: &mut PersistentGameSettings, + ) -> Self { + Self { + show_debug_info: false, + } + } + + pub async fn render_bartender_cutscene_frame( + &mut self, + raylib: &mut RaylibHandle, + rl_thread: &RaylibThread, + discord: &DiscordChannel, + global_resources: &GlobalResources, + constants: &ProjectConstants, + audio_subsystem: &mut RaylibAudio, + ) -> MenuStateSignal { + // Get a drawing handle + let mut draw = raylib.begin_drawing(rl_thread); + + // Clear the screen + draw.clear_background(Color::WHITE); + + //Obtain mouse position + let mouse_x = draw.get_mouse_x(); + let mouse_y = draw.get_mouse_y(); + + // Optionally display debug info + if draw.is_key_pressed(KeyboardKey::KEY_F3) { + self.show_debug_info = !self.show_debug_info; + } + if self.show_debug_info { + // Draw FPS and mouse location + draw.draw_fps(10, 10); + draw.draw_text( + format!("Mouse position: ({}, {})", mouse_x, mouse_y).as_str(), + 10, + 30, + 20, + Color::GREEN, + ); + } + + // Title + draw.draw_text("INTRO CUTSCENE GOES HERE", 100, 90, 60, Color::BLACK); + draw.draw_text("Press SPACE to skip", 100, 600, 20, Color::BLACK); + + // Let the user leave this cutscene by pressing space + if draw.is_key_pressed(KeyboardKey::KEY_SPACE) { + return MenuStateSignal::StartGame; + } + + // Return MenuStateSignal::DoMainMenu if you want to return to the main menu + // Return MenuStateSignal::StartGame if you want the game to start. + // Otherwise, keep returning MenuStateSignal::DoIntroCutscene + return MenuStateSignal::DoIntroCutscene; + } + + pub async fn render_melted_cutscene_frame( + &mut self, + raylib: &mut RaylibHandle, + rl_thread: &RaylibThread, + discord: &DiscordChannel, + global_resources: &GlobalResources, + constants: &ProjectConstants, + audio_subsystem: &mut RaylibAudio, + playtime: &Duration, + ) -> MenuStateSignal { + // Get a drawing handle + let mut draw = raylib.begin_drawing(rl_thread); + + // Clear the screen + draw.clear_background(Color::WHITE); + + //Obtain mouse position + let mouse_x = draw.get_mouse_x(); + let mouse_y = draw.get_mouse_y(); + + // Optionally display debug info + if draw.is_key_pressed(KeyboardKey::KEY_F3) { + self.show_debug_info = !self.show_debug_info; + } + if self.show_debug_info { + // Draw FPS and mouse location + draw.draw_fps(10, 10); + draw.draw_text( + format!("Mouse position: ({}, {})", mouse_x, mouse_y).as_str(), + 10, + 30, + 20, + Color::GREEN, + ); + } + + // Title + draw.draw_text("MELTY CUTSCENE GOES HERE", 100, 90, 60, Color::BLACK); + draw.draw_text( + &format!("This took you {} seconds", playtime.num_seconds()), + 100, + 600, + 20, + Color::BLACK, + ); + draw.draw_text("Press SPACE to skip", 100, 680, 20, Color::BLACK); + + // Let the user leave this cutscene by pressing space + if draw.is_key_pressed(KeyboardKey::KEY_SPACE) { + return MenuStateSignal::DoMainMenu; + } + + // Return MenuStateSignal::DoMainMenu if you want to return to the main menu + // Otherwise, keep returning MenuStateSignal::DoMeltedDeathCutscene + return MenuStateSignal::DoMeltedDeathCutscene { + playtime: playtime.clone(), + }; + } + + pub async fn render_finished_cutscene_frame( + &mut self, + raylib: &mut RaylibHandle, + rl_thread: &RaylibThread, + discord: &DiscordChannel, + global_resources: &GlobalResources, + constants: &ProjectConstants, + audio_subsystem: &mut RaylibAudio, + playtime: &Duration, + ) -> MenuStateSignal { + // Get a drawing handle + let mut draw = raylib.begin_drawing(rl_thread); + + // Clear the screen + draw.clear_background(Color::WHITE); + + //Obtain mouse position + let mouse_x = draw.get_mouse_x(); + let mouse_y = draw.get_mouse_y(); + + // Optionally display debug info + if draw.is_key_pressed(KeyboardKey::KEY_F3) { + self.show_debug_info = !self.show_debug_info; + } + if self.show_debug_info { + // Draw FPS and mouse location + draw.draw_fps(10, 10); + draw.draw_text( + format!("Mouse position: ({}, {})", mouse_x, mouse_y).as_str(), + 10, + 30, + 20, + Color::GREEN, + ); + } + + // Title + draw.draw_text("END CUTSCENE GOES HERE", 100, 90, 60, Color::BLACK); + draw.draw_text( + &format!("This took you {} seconds", playtime.num_seconds()), + 100, + 600, + 20, + Color::BLACK, + ); + draw.draw_text("Press SPACE to skip", 100, 680, 20, Color::BLACK); + + // Let the user leave this cutscene by pressing space + if draw.is_key_pressed(KeyboardKey::KEY_SPACE) { + return MenuStateSignal::DoMainMenu; + } + + // Return MenuStateSignal::DoMainMenu if you want to return to the main menu + // Otherwise, keep returning MenuStateSignal::DoFinishedCutscene + return MenuStateSignal::DoFinishedCutscene { + playtime: playtime.clone(), + }; + } + + pub async fn render_ocean_cutscene_frame( + &mut self, + raylib: &mut RaylibHandle, + rl_thread: &RaylibThread, + discord: &DiscordChannel, + global_resources: &GlobalResources, + constants: &ProjectConstants, + audio_subsystem: &mut RaylibAudio, + playtime: &Duration, + ) -> MenuStateSignal { + // Get a drawing handle + let mut draw = raylib.begin_drawing(rl_thread); + + // Clear the screen + draw.clear_background(Color::WHITE); + + //Obtain mouse position + let mouse_x = draw.get_mouse_x(); + let mouse_y = draw.get_mouse_y(); + + // Optionally display debug info + if draw.is_key_pressed(KeyboardKey::KEY_F3) { + self.show_debug_info = !self.show_debug_info; + } + if self.show_debug_info { + // Draw FPS and mouse location + draw.draw_fps(10, 10); + draw.draw_text( + format!("Mouse position: ({}, {})", mouse_x, mouse_y).as_str(), + 10, + 30, + 20, + Color::GREEN, + ); + } + + // Title + draw.draw_text("OCEAN CUTSCENE GOES HERE", 100, 90, 60, Color::BLACK); + draw.draw_text( + &format!("This took you {} seconds", playtime.num_seconds()), + 100, + 600, + 20, + Color::BLACK, + ); + draw.draw_text("Press SPACE to skip", 100, 680, 20, Color::BLACK); + + // Let the user leave this cutscene by pressing space + if draw.is_key_pressed(KeyboardKey::KEY_SPACE) { + return MenuStateSignal::DoMainMenu; + } + + // Return MenuStateSignal::DoMainMenu if you want to return to the main menu + // Otherwise, keep returning MenuStateSignal::DoOceanCutscene + return MenuStateSignal::DoOceanCutscene { + playtime: playtime.clone(), + }; + } +} diff --git a/game/game_logic/src/scenes/main_menu.rs b/game/game_logic/src/scenes/main_menu.rs index 3ff370e9..58d39cf9 100644 --- a/game/game_logic/src/scenes/main_menu.rs +++ b/game/game_logic/src/scenes/main_menu.rs @@ -1,5 +1,6 @@ //! This scene encompasses the main menu system +use chrono::Duration; use na::Vector1; use nalgebra as na; use raylib::{ @@ -23,6 +24,10 @@ pub enum MenuStateSignal { DoCredits, DoLeaderboard, DoPauseMenu, + DoIntroCutscene, + DoMeltedDeathCutscene { playtime: Duration }, + DoFinishedCutscene { playtime: Duration }, + DoOceanCutscene { playtime: Duration }, } #[derive(Debug)] @@ -119,7 +124,7 @@ impl MainMenu { draw.draw_text("Start Game", 100, 190, 34, label_colors); if draw.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) { audio_subsystem.play_sound(&global_resources.button_click_sound); - return MenuStateSignal::StartGame; + return MenuStateSignal::DoIntroCutscene; } } @@ -364,7 +369,7 @@ impl MainMenu { return MenuStateSignal::QuitGame; } } - // Return MenuStateSignal::StartGame if you want the game to start. + // Return MenuStateSignal::DoIntroCutscene if you want the game to start. // Otherwise, keep returning MenuStateSignal::DoMainMenu until the player clicks the start button return MenuStateSignal::DoMainMenu; } diff --git a/game/game_logic/src/scenes/mod.rs b/game/game_logic/src/scenes/mod.rs index 00eb5d87..b27dfefa 100644 --- a/game/game_logic/src/scenes/mod.rs +++ b/game/game_logic/src/scenes/mod.rs @@ -13,11 +13,13 @@ use crate::{ }; use self::{ + cutscenes::CutScenes, main_menu::{MainMenu, MenuStateSignal}, pause_menu::PauseMenu, player_interaction::PlayableScene, test_fox::TestFoxScene, }; +mod cutscenes; mod main_menu; mod pause_menu; mod player_interaction; @@ -34,6 +36,7 @@ pub struct SceneRenderDelegate { scene_playable: PlayableScene, scene_main_menu: MainMenu, scene_pause_menu: PauseMenu, + scene_cutscenes: CutScenes, } impl SceneRenderDelegate { @@ -51,6 +54,7 @@ impl SceneRenderDelegate { let scene_playable = PlayableScene::new(raylib, rl_thread, constants); let scene_main_menu = MainMenu::new(raylib, rl_thread, constants, game_settings); let scene_pause_menu = PauseMenu::new(raylib, rl_thread, constants, game_settings); + let scene_cutscenes = CutScenes::new(raylib, rl_thread, constants, game_settings); Self { menu_control_signal: MenuStateSignal::DoMainMenu, @@ -60,6 +64,7 @@ impl SceneRenderDelegate { scene_playable, scene_main_menu, scene_pause_menu, + scene_cutscenes, } } @@ -77,9 +82,10 @@ impl SceneRenderDelegate { save_state: &mut GameSaveState, ) { // Render the main menu if in it, otherwise, render the game - match self.menu_control_signal { + match &self.menu_control_signal { MenuStateSignal::StartGame => { - self.menu_control_signal = self.scene_playable + self.menu_control_signal = self + .scene_playable .render_frame( raylib, rl_thread, @@ -159,6 +165,61 @@ impl SceneRenderDelegate { ) .await; } + MenuStateSignal::DoIntroCutscene => { + self.menu_control_signal = self + .scene_cutscenes + .render_bartender_cutscene_frame( + raylib, + rl_thread, + discord, + global_resources, + constants, + &mut self.audio_subsystem, + ) + .await; + } + MenuStateSignal::DoMeltedDeathCutscene { playtime } => { + self.menu_control_signal = self + .scene_cutscenes + .render_melted_cutscene_frame( + raylib, + rl_thread, + discord, + global_resources, + constants, + &mut self.audio_subsystem, + playtime, + ) + .await; + } + MenuStateSignal::DoFinishedCutscene { playtime } => { + self.menu_control_signal = self + .scene_cutscenes + .render_finished_cutscene_frame( + raylib, + rl_thread, + discord, + global_resources, + constants, + &mut self.audio_subsystem, + playtime, + ) + .await; + } + MenuStateSignal::DoOceanCutscene { playtime } => { + self.menu_control_signal = self + .scene_cutscenes + .render_ocean_cutscene_frame( + raylib, + rl_thread, + discord, + global_resources, + constants, + &mut self.audio_subsystem, + playtime, + ) + .await; + } } } } diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index 2ccdf3ed..f9a48689 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -1,5 +1,6 @@ //! This scene encompasses all of the game where the player can walk around. +use chrono::{DateTime, Utc}; use nalgebra as na; use raylib::prelude::*; use std::time::SystemTime; @@ -25,6 +26,7 @@ pub struct PlayableScene { game_soundtrack: Music, world_colliders: Vec, show_debug_info: bool, + play_start_time: DateTime, } impl PlayableScene { @@ -64,6 +66,7 @@ impl PlayableScene { game_soundtrack, world_colliders, show_debug_info: false, + play_start_time: Utc::now(), } } @@ -91,6 +94,7 @@ impl PlayableScene { .await .unwrap(); self.has_updated_discord_rpc = true; + self.play_start_time = Utc::now(); } // Ensure the game soundtrack is playing @@ -111,6 +115,17 @@ impl PlayableScene { self.draw_ui(&mut draw, constants); + // NOTE: If you want to trigger a cutscene, do it here by using one of: + // return MenuStateSignal::DoFinishedCutscene { + // playtime: Utc::now().signed_duration_since(self.play_start_time), + // }; + // return MenuStateSignal::DoMeltedDeathCutscene { + // playtime: Utc::now().signed_duration_since(self.play_start_time), + // }; + // return MenuStateSignal::DoOceanCutscene { + // playtime: Utc::now().signed_duration_since(self.play_start_time), + // }; + // A little hack to make pausing work if draw.is_key_pressed(KeyboardKey::KEY_ESCAPE) { return MenuStateSignal::DoPauseMenu; @@ -124,8 +139,12 @@ impl PlayableScene { let mut ctx2d = draw.begin_mode2D(self.camera); // Render the map - self.world_map - .render_map(&mut ctx2d, &self.camera, self.show_debug_info, self.player.position); + self.world_map.render_map( + &mut ctx2d, + &self.camera, + self.show_debug_info, + self.player.position, + ); let player_size = (constants.tile_size as f32 * constants.player.start_size * self.player.size) as i32;