Add cutscene support to the game

This commit is contained in:
Evan Pratten 2022-04-03 17:12:51 -04:00
parent 5e29a94b60
commit 21e4dc7daa
4 changed files with 355 additions and 6 deletions

View File

@ -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(),
};
}
}

View File

@ -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;
}

View File

@ -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;
}
}
}
}

View File

@ -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<WorldSpaceObjectCollider>,
show_debug_info: bool,
play_start_time: DateTime<Utc>,
}
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;