This repository has been archived on 2022-04-04. You can view files and clone it, but cannot push or open issues or pull requests.
ludum-dare-50/game/game_logic/src/scenes/player_interaction.rs

365 lines
13 KiB
Rust

//! 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;
use crate::{
asset_manager::{load_music_from_internal_data, load_sound_from_internal_data},
discord::{DiscordChannel, DiscordRpcSignal},
global_resource_package::GlobalResources,
model::{
player::Player,
world_object::{ObjectCollider, WorldSpaceObjectCollider},
},
project_constants::ProjectConstants,
rendering::utilities::{anim_texture::AnimatedTexture, map_render::MapRenderer},
};
use super::main_menu::MenuStateSignal;
#[derive(Debug)]
pub struct PlayableScene {
pub has_updated_discord_rpc: bool,
player: Player,
world_map: MapRenderer,
camera: raylib::camera::Camera2D,
last_update: SystemTime,
game_soundtrack: Music,
world_colliders: Vec<WorldSpaceObjectCollider>,
show_debug_info: bool,
play_start_time: DateTime<Utc>,
}
impl PlayableScene {
/// Construct a new `PlayableScene`
pub fn new(
raylib_handle: &mut raylib::RaylibHandle,
thread: &raylib::RaylibThread,
constants: &ProjectConstants,
) -> Self {
let map_renderer = MapRenderer::new(
"map_gameMap.tmx",
"map_gameMap.objects.json",
raylib_handle,
thread,
)
.unwrap();
let world_colliders = map_renderer.get_world_colliders();
// Load the game music
let game_soundtrack =
load_music_from_internal_data(thread, "assets/audio/gameSoundtrack.mp3").unwrap();
Self {
has_updated_discord_rpc: false,
player: Player::new(na::Vector2::new(
10.0 * constants.tile_size as f32,
-10.0 * constants.tile_size as f32,
)),
world_map: map_renderer,
camera: raylib::camera::Camera2D {
target: raylib::math::Vector2 { x: 0.0, y: 0.0 },
offset: raylib::math::Vector2 { x: 0.0, y: 0.0 },
rotation: 0.0,
zoom: 1.0,
},
last_update: SystemTime::UNIX_EPOCH,
game_soundtrack,
world_colliders,
show_debug_info: false,
play_start_time: Utc::now(),
}
}
/// Handler for each frame
pub async fn render_frame(
&mut self,
raylib: &mut raylib::RaylibHandle,
rl_thread: &raylib::RaylibThread,
discord: &DiscordChannel,
global_resources: &GlobalResources,
constants: &ProjectConstants,
audio_subsystem: &mut RaylibAudio,
) -> MenuStateSignal {
// Handle updating discord RPC
if !self.has_updated_discord_rpc {
discord
.send(DiscordRpcSignal::BeginGameTimer)
.await
.unwrap();
discord
.send(DiscordRpcSignal::ChangeDetails {
details: "Playing the game".to_string(),
party_status: None,
})
.await
.unwrap();
self.has_updated_discord_rpc = true;
self.play_start_time = Utc::now();
}
// Ensure the game soundtrack is playing
if !audio_subsystem.is_music_playing(&self.game_soundtrack) {
debug!("Playing game soundtrack");
audio_subsystem.play_music_stream(&mut self.game_soundtrack);
} else {
audio_subsystem.update_music_stream(&mut self.game_soundtrack);
}
// Get a drawing handle
let mut draw = raylib.begin_drawing(rl_thread);
// Clear the screen
draw.clear_background(Color::WHITE);
self.draw_world(&mut draw, constants);
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;
} else {
return MenuStateSignal::StartGame;
}
}
pub fn draw_world(&mut self, draw: &mut RaylibDrawHandle, constants: &ProjectConstants) {
// Begin camera mode
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,
);
let player_size =
(constants.tile_size as f32 * constants.player.start_size * self.player.size) as i32;
ctx2d.draw_rectangle(
self.player.position[0] as i32 - player_size / 2,
self.player.position[1] as i32 * -1 - player_size / 2,
player_size,
player_size,
Color::LIGHTBLUE,
);
}
pub fn draw_ui(&mut self, draw: &mut RaylibDrawHandle, constants: &ProjectConstants) {
// 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,
);
draw.draw_text(
format!("player: ({}, {}) size: {} map: ({}, {})", self.player.position.x,self.player.position.y,self.player.size,self.world_map.get_map_size().x,self.world_map.get_map_size().y).as_str(),
10,
50,
20,
Color::GREEN,
);
}
draw.draw_rectangle(draw.get_screen_width() / 2 - 225, 0, 450, 40, Color::WHITE);
draw.draw_text(
"Unregistered HyperCam 2",
draw.get_screen_width() / 2 - 215,
0,
32,
Color::BLACK,
);
}
// Physics
pub async fn update_physics(
&mut self,
raylib: &raylib::RaylibHandle,
constants: &ProjectConstants,
) {
// Get time since last physics update
let time = SystemTime::now();
let elapsed = time
.duration_since(self.last_update)
.expect("Time Appears to Have Moved Backwards!");
self.last_update = time;
let delta_time = elapsed.as_millis() as f32 / 1000.0; // Physics will be scaled by this value
let player = &mut self.player;
// NOTE: This is how to check friction and temperature
let current_friction = self.world_map.sample_friction_at(player.position);
let current_temperature = self.world_map.sample_temperature_at(player.position);
let map_size = self.world_map.get_map_size();
// TODO: You can access the colission list with: self.world_colliders
// Get input direction components
let h_axis = raylib.is_key_down(KeyboardKey::KEY_D) as i8
- raylib.is_key_down(KeyboardKey::KEY_A) as i8;
let v_axis = raylib.is_key_down(KeyboardKey::KEY_W) as i8
- raylib.is_key_down(KeyboardKey::KEY_S) as i8;
if h_axis != 0 || v_axis != 0 {
// Normalize input and accelerate in desired direction
let direction = na::Vector2::new(h_axis as f32, v_axis as f32).normalize();
player.velocity += &direction.xy()
* constants.player.acceleration as f32
* constants.tile_size as f32
* delta_time;
}
if player.velocity.magnitude() != 0.0 {
player.velocity -= player.velocity.normalize()
* constants.player.deceleration as f32
* constants.tile_size as f32
* delta_time;
if player.velocity.magnitude() < 1.0 {
player.velocity.set_magnitude(0.0);
}
}
if ((constants.player.max_velocity * constants.tile_size) as f32)
< player.velocity.magnitude()
{
player
.velocity
.set_magnitude((constants.player.max_velocity * constants.tile_size) as f32);
}
let velocity_modifier = &player.velocity * delta_time;
let player_size =
(constants.tile_size as f32 * constants.player.start_size * player.size / 2.0) as f32;
player.position.x += velocity_modifier.x;
for i in &self.world_colliders {
if player.position.x - player_size <= i.position.x + i.size.x / 2.0
&& player.position.x + player_size >= i.position.x + i.size.x / 2.0
&& player.position.y - player_size<= i.position.y + i.size.y / 2.0
&& player.position.y + player_size >= i.position.y + i.size.x / 2.0
{
if player.velocity.x < 0.0 {
player.position.x = i.position.x + i.size.x / 2.0 + player_size;
} else if player.velocity.x > 0.0 {
player.position.x = i.position.x - i.size.x / 2.0 - player_size;
}
player.velocity.x = 0.0;
break;
}
}
if player.position.x - player_size < 0.0 || player.position.x + player_size > self.world_map.get_map_size().x {
if player.velocity.x < 0.0 {
player.position.x = player_size;
} else if player.velocity.x > 0.0 {
player.position.x = self.world_map.get_map_size().x - player_size;
}
player.velocity.x = 0.0;
}
player.position.y += velocity_modifier.y;
for i in &self.world_colliders {
if player.position.x - player_size <= i.position.x + i.size.x / 2.0
&& player.position.x + player_size >= i.position.x + i.size.x / 2.0
&& player.position.y - player_size<= i.position.y + i.size.y / 2.0
&& player.position.y + player_size >= i.position.y + i.size.x / 2.0
{
if player.velocity.y < 0.0 {
player.position.y = i.position.y + i.size.y / 2.0 + player_size;
} else if player.velocity.x > 0.0 {
player.position.y = i.position.y - i.size.y / 2.0 - player_size;
}
break;
}
}
if player.position.y + player_size > 0.0 || player.position.y - player_size < -self.world_map.get_map_size().y {
if player.velocity.y > 0.0 {
player.position.y = -player_size;
} else if player.velocity.y < 0.0 {
player.position.y = -self.world_map.get_map_size().y + player_size;
}
player.velocity.y = 0.0;
}
self.update_camera(raylib);
}
// Update the camera
pub fn update_camera(&mut self, raylib: &raylib::RaylibHandle) {
// Bounding box
let bbox = na::Vector2::new(0.2, 0.2);
// Get bounding box dimensions on the screen
let bbox_screen_min: raylib::math::Vector2 = (((na::Vector2::new(1.0, 1.0) - bbox) * 0.5)
.component_mul(&na::Vector2::new(
raylib.get_screen_width() as f32,
raylib.get_screen_height() as f32,
)))
.into();
let bbox_screen_max: raylib::math::Vector2 = (((na::Vector2::new(1.0, 1.0) + bbox) * 0.5)
.component_mul(&na::Vector2::new(
raylib.get_screen_width() as f32,
raylib.get_screen_height() as f32,
)))
.into();
// Get bounding box in world space
let mut bbox_world_min = raylib.get_screen_to_world2D(bbox_screen_min, self.camera);
let mut bbox_world_max = raylib.get_screen_to_world2D(bbox_screen_max, self.camera);
// Invert y
bbox_world_min.y *= -1.0;
bbox_world_max.y *= -1.0;
self.camera.offset = bbox_screen_min;
if self.player.position.x < bbox_world_min.x {
self.camera.target.x = self.player.position.x;
}
if self.player.position.y > bbox_world_min.y {
self.camera.target.y = -self.player.position.y;
}
if self.player.position.x > bbox_world_max.x {
self.camera.target.x = bbox_world_min.x + (self.player.position.x - bbox_world_max.x);
}
if self.player.position.y < bbox_world_max.y {
self.camera.target.y = bbox_world_max.y - (self.player.position.y + bbox_world_min.y);
}
}
}