Merge remote-tracking branch 'origin/master' into rsninja/fixes

This commit is contained in:
rsninja722 2022-04-04 00:47:25 -04:00
commit b72051ebbe
12 changed files with 390 additions and 177 deletions

View File

@ -1,4 +1,4 @@
# Ludum Dare 50: *unnamed game*
# Ludum Dare 50: *Melting Point*
[![Build Full Release](https://github.com/Ewpratten/ludum-dare-50/actions/workflows/build.yml/badge.svg)](https://github.com/Ewpratten/ludum-dare-50/actions/workflows/build.yml)
## Navigating this repository

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,5 +1,5 @@
{
"game_name": "Ludum Dare 50",
"game_name": "Melting Point",
"base_window_size": [
1080,
720
@ -21,6 +21,7 @@
"max_velocity": 3,
"acceleration": 2,
"deceleration": 1,
"start_size": 0.8
"start_size": 0.8,
"melt_speed": 0.00944443
}
}

View File

@ -21,6 +21,8 @@
//! - If you want to add data to the save state file or settings file, check out the [`persistent`](persistent/index.html) module.
#![doc(issue_tracker_base_url = "https://github.com/Ewpratten/ludum-dare-50/issues/")]
use discord_sdk::activity::Assets;
use crate::{
asset_manager::load_json_structure,
discord::{DiscordRpcSignal, DiscordRpcThreadHandle},
@ -69,6 +71,12 @@ pub async fn entrypoint(force_recreate_savefiles: bool) {
.expect("Failed to connect to Discord RPC");
let event_loop_discord_tx = discord.get_channel();
let discord_task_handle = discord.begin_thread_non_blocking();
event_loop_discord_tx
.send(DiscordRpcSignal::ChangeAssets(Assets::default().large(
project_constants.discord.artwork.get("logo").unwrap(),
Some(""),
)))
.await.unwrap();
// Blocking call to the graphics rendering loop.
rendering::event_loop::handle_graphics_blocking(

View File

@ -1,22 +1,53 @@
use nalgebra as na;
use raylib::prelude::*;
#[derive(Debug, Clone)]
use crate::asset_manager::load_texture_from_internal_data;
#[derive(Debug)]
pub struct Player {
pub position: na::Vector2<f32>,
pub velocity: na::Vector2<f32>,
pub size: f32,
pub active_texture: i32,
pub textures: Vec<Texture2D>,
}
impl Player {
/// Construct a new player.
pub fn new(position: na::Vector2<f32>) -> Self {
pub fn new(
raylib_handle: &mut raylib::RaylibHandle,
thread: &raylib::RaylibThread,
position: na::Vector2<f32>,
) -> Self {
// Load all the textures
let textures = vec![
load_texture_from_internal_data(
raylib_handle,
thread,
"assets/chr/chr_cubee/chr_cubeeLarge.png",
)
.unwrap(),
load_texture_from_internal_data(
raylib_handle,
thread,
"assets/chr/chr_cubee/chr_cubeeMedium.png",
)
.unwrap(),
load_texture_from_internal_data(
raylib_handle,
thread,
"assets/chr/chr_cubee/chr_cubeeSmall.png",
)
.unwrap(),
];
Self {
position,
velocity: na::Vector2::zeros(),
size: 1.0,
size: 1.0,
active_texture: 0,
textures,
}
}
}

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use nalgebra as na;
use raylib::{texture::Texture2D, RaylibHandle, RaylibThread};
use std::collections::HashMap;
use crate::{
asset_manager::{load_json_structure, load_texture_from_internal_data},
@ -34,6 +34,8 @@ pub struct WorldObjectPackage {
pub top_animated_textures: HashMap<String, AnimatedTexture>,
/// A list of colliders in the world. We pre-solve these to make comput happy :)
pub world_space_colliders: Vec<WorldSpaceObjectCollider>,
/// A list of footprints in the world. We pre-solve these to make comput happy :)
pub world_space_footprints: Vec<WorldSpaceObjectCollider>,
}
impl WorldObjectPackage {
@ -52,6 +54,7 @@ impl WorldObjectPackage {
let mut bottom_animated_textures = HashMap::new();
let mut top_animated_textures = HashMap::new();
let mut world_space_colliders: Vec<WorldSpaceObjectCollider> = Vec::new();
let mut world_space_footprints: Vec<WorldSpaceObjectCollider> = Vec::new();
for reference in &object_references {
// If this is a new object, load it.
let object_key = reference.into_key();
@ -118,6 +121,24 @@ impl WorldObjectPackage {
world_space_colliders.push(world_space_collider);
}
// Keep track of all the footprints in the world
for collider in &object_definition.footprint {
// Get the object's position
let object_position = reference.get_world_space_position();
// Convert the collider's position to world space
let mut world_space_collider = WorldSpaceObjectCollider {
position: (object_position + collider.position),
size: collider.size,
};
// Invert the Y axis of the collider's position
world_space_collider.position.y = -world_space_collider.position.y;
// Add the collider to the list
world_space_footprints.push(world_space_collider);
}
// Store the object definition
object_definitions.insert(object_key.to_string(), object_definition);
}
@ -131,6 +152,7 @@ impl WorldObjectPackage {
bottom_animated_textures,
top_animated_textures,
world_space_colliders,
world_space_footprints,
})
}
}

View File

@ -43,6 +43,9 @@ pub struct PlayerConstants {
/// Starting size of player in tiles
pub start_size: f32,
/// Base melting speed in percent per second
pub melt_speed: f32,
}
/// This structure is filled with the contents of `dist/project-constants.json` at runtime

View File

@ -50,7 +50,7 @@ pub async fn handle_graphics_blocking<ConfigBuilder>(
// Set up audio
debug!("Set up Audio");
let audio_subsystem = RaylibAudio::init_audio_device();
audio_subsystem.set_master_volume(0.4);
audio_subsystem.set_master_volume(game_settings.volume.unwrap_or(0.5));
// Set up the internal screens
let mut loading_screen = crate::rendering::screens::loading_screen::LoadingScreen::new();

View File

@ -279,6 +279,9 @@ impl MapRenderer {
camera,
);
let player_position = na::Vector2::new(player_position.x, player_position.y * -1.0);
// Get the tile width and height
let tile_width = 128;
let tile_height = 128;
// Handle each layer from the bottom up
for layer in self.map.layers() {
@ -289,10 +292,6 @@ impl MapRenderer {
let mut sampler_x = 0;
let mut sampler_y = 0;
// Get the tile width and height
let tile_width = 128;
let tile_height = 128;
// Loop until we have covered all tiles on the screen
for y in (world_win_top_left.y as i64)..(world_win_bottom_right.y as i64) {
// Convert the pixel coordinates to tile coordinates
@ -331,157 +330,178 @@ impl MapRenderer {
Color::WHITE,
);
}
}
}
}
}
}
tiled::LayerType::ObjectLayer(_) => todo!(),
tiled::LayerType::ImageLayer(_) => todo!(),
tiled::LayerType::GroupLayer(_) => todo!(),
}
}
// Check if there is an object at this tile
for obj_ref in &self.world_objects.object_references {
if obj_ref.get_tile_space_position().x == sampler_x as f32
&& obj_ref.get_tile_space_position().y
== sampler_y as f32
{
// Get access to the actual object definition
let object_key = obj_ref.into_key();
// debug!("Found object: {}", object_key);
let obj_def = self
.world_objects
.object_definitions
.get(&object_key)
.unwrap();
// Keep track of our sampler X and Y values
let mut sampler_x = 0;
let mut sampler_y = 0;
// We need to render the base layer of the object
if obj_def.bottom_texture.animated.unwrap_or(false) {
let tex = self
.world_objects
.bottom_animated_textures
.get_mut(&object_key)
.unwrap();
tex.render_automatic(
draw_handle,
obj_ref.get_world_space_position()
- (tex.size() / 2.0),
None,
Some(tex.size() / 2.0),
Some(obj_ref.rotation_degrees),
None,
);
} else {
let tex = self
.world_objects
.bottom_static_textures
.get_mut(&object_key)
.unwrap();
let p: Vector2 =
obj_ref.get_world_space_position().into();
let r1 = Rectangle {
x: 0.0,
y: 0.0,
width: tex.width as f32,
height: tex.height as f32,
};
let r2 = Rectangle {
x: p.x,
y: p.y,
width: tex.width as f32,
height: tex.height as f32,
};
// Loop until we have covered all tiles on the screen
for y in (world_win_top_left.y as i64)..(world_win_bottom_right.y as i64) {
// Convert the pixel coordinates to tile coordinates
let tile_y = (y as f32 / tile_height as f32).floor() as i32;
draw_handle.draw_texture_pro(
&tex,
r1,
r2,
Vector2::new(
tex.width as f32 / 2.0,
tex.height as f32 / 2.0,
),
obj_ref.rotation_degrees,
Color::WHITE,
);
}
// If we are looking at a new tile, update the sampler
if sampler_y != tile_y {
sampler_y = tile_y;
// If needed we can render the top layer of the object
if let Some(top_texture) = &obj_def.top_texture {
// We need to detect if the player is in the footprint of the object
let mut tint = Color::WHITE;
if let Some(footprint_radius) =
obj_def.visualization_radius
{
let player_dist_to_object = (obj_ref
.get_world_space_position()
- player_position)
.norm();
// debug!(
// "Player dist to object: {}",
// player_dist_to_object
// );
if player_dist_to_object <= footprint_radius {
tint.a = 128;
}
}
for x in (world_win_top_left.x as i64)..(world_win_bottom_right.x as i64) {
// Convert the pixel coordinates to tile coordinates
let tile_x = (x as f32 / tile_width as f32).floor() as i32;
// debug!("Tile: ({}, {})", tile_x, tile_y);
if top_texture.animated.unwrap_or(false) {
let tex = self
.world_objects
.top_animated_textures
.get_mut(&object_key)
.unwrap();
tex.render_automatic(
draw_handle,
obj_ref.get_world_space_position()
- (tex.size() / 2.0),
None,
Some(tex.size() / 2.0),
Some(obj_ref.rotation_degrees),
Some(tint),
);
} else {
let tex = self
.world_objects
.top_static_textures
.get_mut(&object_key)
.unwrap();
let p: Vector2 =
obj_ref.get_world_space_position().into();
let r1 = Rectangle {
x: 0.0,
y: 0.0,
width: tex.width as f32,
height: tex.height as f32,
};
let r2 = Rectangle {
x: p.x,
y: p.y,
width: tex.width as f32,
height: tex.height as f32,
};
// If we are looking at a new tile, update the sampler
if sampler_x != tile_x {
sampler_x = tile_x;
draw_handle.draw_texture_pro(
&tex,
r1,
r2,
Vector2::new(
tex.width as f32 / 2.0,
tex.height as f32 / 2.0,
),
obj_ref.rotation_degrees,
tint,
);
}
}
// Check if there is an object at this tile
for obj_ref in &self.world_objects.object_references {
if obj_ref.get_tile_space_position().x == sampler_x as f32
&& obj_ref.get_tile_space_position().y == sampler_y as f32
{
// Get access to the actual object definition
let object_key = obj_ref.into_key();
// debug!("Found object: {}", object_key);
let obj_def = self
.world_objects
.object_definitions
.get(&object_key)
.unwrap();
// We need to render the base layer of the object
if obj_def.bottom_texture.animated.unwrap_or(false) {
let tex = self
.world_objects
.bottom_animated_textures
.get_mut(&object_key)
.unwrap();
tex.render_automatic(
draw_handle,
obj_ref.get_world_space_position() - (tex.size() / 2.0),
None,
Some(tex.size() / 2.0),
Some(obj_ref.rotation_degrees),
None,
);
} else {
let tex = self
.world_objects
.bottom_static_textures
.get_mut(&object_key)
.unwrap();
let p: Vector2 = obj_ref.get_world_space_position().into();
let r1 = Rectangle {
x: 0.0,
y: 0.0,
width: tex.width as f32,
height: tex.height as f32,
};
let r2 = Rectangle {
x: p.x,
y: p.y,
width: tex.width as f32,
height: tex.height as f32,
};
draw_handle.draw_texture_pro(
&tex,
r1,
r2,
Vector2::new(
tex.width as f32 / 2.0,
tex.height as f32 / 2.0,
),
obj_ref.rotation_degrees,
Color::WHITE,
);
}
// If needed we can render the top layer of the object
if let Some(top_texture) = &obj_def.top_texture {
// We need to detect if the player is in the footprint of the object
let mut tint = Color::WHITE;
if let Some(footprint_radius) = obj_def.visualization_radius {
let player_dist_to_object =
(obj_ref.get_world_space_position() - player_position)
.norm();
// debug!(
// "Player dist to object: {}",
// player_dist_to_object
// );
if player_dist_to_object <= footprint_radius {
tint.a = 128;
}
}
if show_debug_grid {
draw_handle.draw_rectangle_lines(
tile_x * tile_width as i32,
tile_y * tile_height as i32,
self.map.tile_width as i32,
self.map.tile_height as i32,
Color::RED,
if top_texture.animated.unwrap_or(false) {
let tex = self
.world_objects
.top_animated_textures
.get_mut(&object_key)
.unwrap();
tex.render_automatic(
draw_handle,
obj_ref.get_world_space_position() - (tex.size() / 2.0),
None,
Some(tex.size() / 2.0),
Some(obj_ref.rotation_degrees),
Some(tint),
);
} else {
let tex = self
.world_objects
.top_static_textures
.get_mut(&object_key)
.unwrap();
let p: Vector2 = obj_ref.get_world_space_position().into();
let r1 = Rectangle {
x: 0.0,
y: 0.0,
width: tex.width as f32,
height: tex.height as f32,
};
let r2 = Rectangle {
x: p.x,
y: p.y,
width: tex.width as f32,
height: tex.height as f32,
};
draw_handle.draw_texture_pro(
&tex,
r1,
r2,
Vector2::new(
tex.width as f32 / 2.0,
tex.height as f32 / 2.0,
),
obj_ref.rotation_degrees,
tint,
);
draw_handle.draw_pixel(x as i32, y as i32, Color::BLUE);
}
}
}
}
if show_debug_grid {
draw_handle.draw_rectangle_lines(
tile_x * tile_width as i32,
tile_y * tile_height as i32,
self.map.tile_width as i32,
self.map.tile_height as i32,
Color::RED,
);
draw_handle.draw_pixel(x as i32, y as i32, Color::BLUE);
}
}
// for collider in &self.world_objects.world_space_colliders {
// draw_handle.draw_rectangle_lines(
@ -493,9 +513,6 @@ impl MapRenderer {
// );
// }
}
tiled::LayerType::ObjectLayer(_) => todo!(),
tiled::LayerType::ImageLayer(_) => todo!(),
tiled::LayerType::GroupLayer(_) => todo!(),
}
}
}

View File

@ -9,6 +9,7 @@ use raylib::{
};
use crate::{
asset_manager::load_texture_from_internal_data,
discord::{DiscordChannel, DiscordRpcSignal},
global_resource_package::GlobalResources,
persistent::settings::PersistentGameSettings,
@ -17,9 +18,24 @@ use crate::{
use super::main_menu::MenuStateSignal;
const MIWU_WHITE: Color = Color {
r: 247,
g: 239,
b: 231,
a: 255,
};
const MIWU_WHITE_V2: Color = Color {
r: 255,
g: 245,
b: 228,
a: 255,
};
#[derive(Debug)]
pub struct CutScenes {
show_debug_info: bool,
intro_art: Texture2D,
melted_art: Texture2D,
}
impl CutScenes {
@ -30,8 +46,24 @@ impl CutScenes {
constants: &ProjectConstants,
game_settings: &mut PersistentGameSettings,
) -> Self {
// Load art
let intro_art = load_texture_from_internal_data(
raylib_handle,
thread,
"assets/cut/cut_intro/cut_intro.png",
)
.unwrap();
let melted_art = load_texture_from_internal_data(
raylib_handle,
thread,
"assets/cut/cut_melty/cut_melty.png",
)
.unwrap();
Self {
show_debug_info: false,
intro_art,
melted_art,
}
}
@ -48,7 +80,7 @@ impl CutScenes {
let mut draw = raylib.begin_drawing(rl_thread);
// Clear the screen
draw.clear_background(Color::WHITE);
draw.clear_background(MIWU_WHITE);
//Obtain mouse position
let mouse_x = draw.get_mouse_x();
@ -71,8 +103,40 @@ impl CutScenes {
}
// 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);
// 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 screen_height = draw.get_screen_height();
let screen_width = draw.get_screen_width();
// Build a rect for the texture
let tex_rect = Rectangle::new(
0.0,
0.0,
self.intro_art.width as f32,
self.intro_art.height as f32,
);
// Draw the texture to the center of the screen.
// Keep in mind, textures are drawn from the top left
// corner, so we need to offset the rect by half the
// texture's width and height.
let dest_rect = Rectangle::new(
(screen_width / 2) as f32 - (tex_rect.width / 2.0),
(screen_height / 2) as f32 - (tex_rect.height / 2.0),
tex_rect.width,
tex_rect.height,
);
// Draw the texture
draw.draw_texture_pro(
&self.intro_art,
&tex_rect,
&dest_rect,
Vector2::zero(),
0.0,
Color::WHITE,
);
// Let the user leave this cutscene by pressing space
if draw.is_key_pressed(KeyboardKey::KEY_SPACE) {
@ -99,7 +163,7 @@ impl CutScenes {
let mut draw = raylib.begin_drawing(rl_thread);
// Clear the screen
draw.clear_background(Color::WHITE);
draw.clear_background(MIWU_WHITE_V2);
//Obtain mouse position
let mouse_x = draw.get_mouse_x();
@ -121,16 +185,48 @@ impl CutScenes {
);
}
// 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,
// // 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 screen_height = draw.get_screen_height();
let screen_width = draw.get_screen_width();
// Build a rect for the texture
let tex_rect = Rectangle::new(
0.0,
0.0,
self.melted_art.width as f32,
self.melted_art.height as f32,
);
// Draw the texture to the center of the screen.
// Keep in mind, textures are drawn from the top left
// corner, so we need to offset the rect by half the
// texture's width and height.
let dest_rect = Rectangle::new(
(screen_width / 2) as f32 - (tex_rect.width / 2.0),
(screen_height / 2) as f32 - (tex_rect.height / 2.0),
tex_rect.width,
tex_rect.height,
);
// Draw the texture
draw.draw_texture_pro(
&self.melted_art,
&tex_rect,
&dest_rect,
Vector2::zero(),
0.0,
Color::WHITE,
);
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) {
@ -158,7 +254,7 @@ impl CutScenes {
let mut draw = raylib.begin_drawing(rl_thread);
// Clear the screen
draw.clear_background(Color::WHITE);
draw.clear_background(MIWU_WHITE);
//Obtain mouse position
let mouse_x = draw.get_mouse_x();
@ -217,7 +313,7 @@ impl CutScenes {
let mut draw = raylib.begin_drawing(rl_thread);
// Clear the screen
draw.clear_background(Color::WHITE);
draw.clear_background(MIWU_WHITE);
//Obtain mouse position
let mouse_x = draw.get_mouse_x();

View File

@ -60,18 +60,25 @@ impl PlayableScene {
let game_soundtrack =
load_music_from_internal_data(thread, "assets/audio/gameSoundtrack.mp3").unwrap();
// Load the player
let player = Player::new(
raylib_handle,
thread,
player_start_position,
);
Self {
has_updated_discord_rpc: false,
player_start_position,
player: Player::new(player_start_position),
world_map: map_renderer,
player,
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,
last_update: SystemTime::now(),
game_soundtrack,
world_colliders,
show_debug_info: false,
@ -127,13 +134,18 @@ 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::DoMeltedDeathCutscene {
// playtime: Utc::now().signed_duration_since(self.play_start_time),
// };
// return MenuStateSignal::DoOceanCutscene {
// playtime: Utc::now().signed_duration_since(self.play_start_time),
// };
// Handle Losing
if self.player.size < 0.15 {
return MenuStateSignal::DoMeltedDeathCutscene {
playtime: Utc::now().signed_duration_since(self.play_start_time),
};
}
// Handle winning
if self
.world_map
@ -181,6 +193,14 @@ impl PlayableScene {
let mouse_x = draw.get_mouse_x();
let mouse_y = draw.get_mouse_y();
let current_temperature = self.world_map.sample_temperature_at(
self.player.position.component_mul(&na::Vector2::new(1.0, -1.0))
);
let mut current_temperature_val: f32 = -247.51879;
if let Some(val) = current_temperature {
current_temperature_val = val;
}
// Optionally display debug info
if draw.is_key_pressed(KeyboardKey::KEY_F3) {
self.show_debug_info = !self.show_debug_info;
@ -224,6 +244,12 @@ impl PlayableScene {
// 32,
// Color::BLACK,
// );
let melt_amount = (current_temperature_val)/(-247.51879);
draw.draw_text(
format!("Funny Temperature: ({})[{}]", current_temperature_val, melt_amount).as_str(),
10, 10, 20, Color::PAPAYAWHIP
);
}
// Physics
@ -242,11 +268,11 @@ impl PlayableScene {
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 current_temperature = self.world_map.sample_temperature_at(
player.position.component_mul(&na::Vector2::new(1.0, -1.0))
);
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
@ -347,6 +373,15 @@ impl PlayableScene {
player.velocity.y = 0.0;
}
let mut current_temperature_val: f32 = -247.51879;
if let Some(val) = current_temperature {
current_temperature_val = val - 273.15;
}
let melt_amount = constants.player.melt_speed * (current_temperature_val)/(-247.51879);
player.size -= melt_amount * delta_time;
self.update_camera(raylib);
}