From 3ed57a17e76602cdf130ef315679dd6779057548 Mon Sep 17 00:00:00 2001 From: Si Bartha Date: Sun, 3 Apr 2022 14:39:14 -0400 Subject: [PATCH 01/17] Shrinkage --- game/game_logic/src/model/player.rs | 11 ++++++++++- game/game_logic/src/scenes/player_interaction.rs | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/game/game_logic/src/model/player.rs b/game/game_logic/src/model/player.rs index 69a7fe9f..7f59fcc3 100644 --- a/game/game_logic/src/model/player.rs +++ b/game/game_logic/src/model/player.rs @@ -1,11 +1,14 @@ use nalgebra as na; +use raylib::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Player { pub position: na::Vector2, pub velocity: na::Vector2, pub size: f32, + pub active_texture: i32, + pub textures: [String; 3], } impl Player { @@ -16,6 +19,12 @@ impl Player { position, velocity: na::Vector2::zeros(), size: 1.0, + active_texture: 0, + textures: [ + "assets/chr/chr_cubee/chr_cubeeLarge.png".to_string(), + "assets/chr/chr_cubee/chr_cubeeMedium.png".to_string(), + "assets/chr/chr_cubee/chr_cubeeSmall.png".to_string() + ] } } diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index bff9ca0a..e3593dcd 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -197,6 +197,8 @@ impl PlayableScene { player.position += velocity_modifier; + player.size -= 0.001; + self.update_camera(raylib); } From c024ee78dd2d21c39818d83635033b3b0f973364 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sun, 3 Apr 2022 14:43:37 -0400 Subject: [PATCH 02/17] Load textures --- game/game_logic/src/model/player.rs | 44 ++++++++++++++----- .../src/scenes/player_interaction.rs | 17 ++++--- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/game/game_logic/src/model/player.rs b/game/game_logic/src/model/player.rs index 7f59fcc3..4b84c14f 100644 --- a/game/game_logic/src/model/player.rs +++ b/game/game_logic/src/model/player.rs @@ -2,30 +2,52 @@ use nalgebra as na; use raylib::prelude::*; +use crate::asset_manager::load_texture_from_internal_data; + #[derive(Debug)] pub struct Player { pub position: na::Vector2, pub velocity: na::Vector2, pub size: f32, pub active_texture: i32, - pub textures: [String; 3], + pub textures: Vec, } impl Player { - /// Construct a new player. - pub fn new(position: na::Vector2) -> Self { + pub fn new( + raylib_handle: &mut raylib::RaylibHandle, + thread: &raylib::RaylibThread, + position: na::Vector2, + ) -> 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, - active_texture: 0, - textures: [ - "assets/chr/chr_cubee/chr_cubeeLarge.png".to_string(), - "assets/chr/chr_cubee/chr_cubeeMedium.png".to_string(), - "assets/chr/chr_cubee/chr_cubeeSmall.png".to_string() - ] + size: 1.0, + active_texture: 0, + textures, } } - } diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index e3593dcd..56906373 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -44,12 +44,19 @@ impl PlayableScene { 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( + // Load the player + let player = Player::new( + raylib_handle, + thread, + na::Vector2::new( 10.0 * constants.tile_size as f32, -10.0 * constants.tile_size as f32, - )), + ), + ); + + Self { + has_updated_discord_rpc: false, + player, world_map: map_renderer, camera: raylib::camera::Camera2D { target: raylib::math::Vector2 { x: 0.0, y: 0.0 }, @@ -197,7 +204,7 @@ impl PlayableScene { player.position += velocity_modifier; - player.size -= 0.001; + player.size -= 0.001; self.update_camera(raylib); } From 75c6c22a63d66b0b7b56dcb79bc252d714c9407a Mon Sep 17 00:00:00 2001 From: Si Bartha Date: Sun, 3 Apr 2022 14:39:14 -0400 Subject: [PATCH 03/17] Shrinkage --- game/game_logic/src/model/player.rs | 11 ++++++++++- game/game_logic/src/scenes/player_interaction.rs | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/game/game_logic/src/model/player.rs b/game/game_logic/src/model/player.rs index 69a7fe9f..7f59fcc3 100644 --- a/game/game_logic/src/model/player.rs +++ b/game/game_logic/src/model/player.rs @@ -1,11 +1,14 @@ use nalgebra as na; +use raylib::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Player { pub position: na::Vector2, pub velocity: na::Vector2, pub size: f32, + pub active_texture: i32, + pub textures: [String; 3], } impl Player { @@ -16,6 +19,12 @@ impl Player { position, velocity: na::Vector2::zeros(), size: 1.0, + active_texture: 0, + textures: [ + "assets/chr/chr_cubee/chr_cubeeLarge.png".to_string(), + "assets/chr/chr_cubee/chr_cubeeMedium.png".to_string(), + "assets/chr/chr_cubee/chr_cubeeSmall.png".to_string() + ] } } diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index 41374b14..3dddabd6 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -343,6 +343,8 @@ impl PlayableScene { player.velocity.y = 0.0; } + player.size -= 0.001; + self.update_camera(raylib); } From 486a14861488eda52ff14795a05fa2259b103e62 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sun, 3 Apr 2022 14:43:37 -0400 Subject: [PATCH 04/17] Load textures --- game/game_logic/src/model/player.rs | 44 ++++++++++++++----- .../src/scenes/player_interaction.rs | 11 ++++- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/game/game_logic/src/model/player.rs b/game/game_logic/src/model/player.rs index 7f59fcc3..4b84c14f 100644 --- a/game/game_logic/src/model/player.rs +++ b/game/game_logic/src/model/player.rs @@ -2,30 +2,52 @@ use nalgebra as na; use raylib::prelude::*; +use crate::asset_manager::load_texture_from_internal_data; + #[derive(Debug)] pub struct Player { pub position: na::Vector2, pub velocity: na::Vector2, pub size: f32, pub active_texture: i32, - pub textures: [String; 3], + pub textures: Vec, } impl Player { - /// Construct a new player. - pub fn new(position: na::Vector2) -> Self { + pub fn new( + raylib_handle: &mut raylib::RaylibHandle, + thread: &raylib::RaylibThread, + position: na::Vector2, + ) -> 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, - active_texture: 0, - textures: [ - "assets/chr/chr_cubee/chr_cubeeLarge.png".to_string(), - "assets/chr/chr_cubee/chr_cubeeMedium.png".to_string(), - "assets/chr/chr_cubee/chr_cubeeSmall.png".to_string() - ] + size: 1.0, + active_texture: 0, + textures, } } - } diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index 3dddabd6..54871bc4 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -60,11 +60,18 @@ 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 }, @@ -343,7 +350,7 @@ impl PlayableScene { player.velocity.y = 0.0; } - player.size -= 0.001; + player.size -= 0.001; self.update_camera(raylib); } From c955f2370bf5dd0a9f9cbf1bf0fa4f55c7f3ff07 Mon Sep 17 00:00:00 2001 From: Si Bartha Date: Sun, 3 Apr 2022 14:39:14 -0400 Subject: [PATCH 05/17] Shrinkage --- game/game_logic/src/model/player.rs | 11 ++++++++++- game/game_logic/src/scenes/player_interaction.rs | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/game/game_logic/src/model/player.rs b/game/game_logic/src/model/player.rs index 69a7fe9f..7f59fcc3 100644 --- a/game/game_logic/src/model/player.rs +++ b/game/game_logic/src/model/player.rs @@ -1,11 +1,14 @@ use nalgebra as na; +use raylib::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Player { pub position: na::Vector2, pub velocity: na::Vector2, pub size: f32, + pub active_texture: i32, + pub textures: [String; 3], } impl Player { @@ -16,6 +19,12 @@ impl Player { position, velocity: na::Vector2::zeros(), size: 1.0, + active_texture: 0, + textures: [ + "assets/chr/chr_cubee/chr_cubeeLarge.png".to_string(), + "assets/chr/chr_cubee/chr_cubeeMedium.png".to_string(), + "assets/chr/chr_cubee/chr_cubeeSmall.png".to_string() + ] } } diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index 9ff4ab04..6c3f41c3 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -347,6 +347,8 @@ impl PlayableScene { player.velocity.y = 0.0; } + player.size -= 0.001; + self.update_camera(raylib); } From 22f50bbd03481dbfd12b3de63e6e62d8b5b8f08b Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sun, 3 Apr 2022 14:43:37 -0400 Subject: [PATCH 06/17] Load textures --- game/game_logic/src/model/player.rs | 44 ++++++++++++++----- .../src/scenes/player_interaction.rs | 11 ++++- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/game/game_logic/src/model/player.rs b/game/game_logic/src/model/player.rs index 7f59fcc3..4b84c14f 100644 --- a/game/game_logic/src/model/player.rs +++ b/game/game_logic/src/model/player.rs @@ -2,30 +2,52 @@ use nalgebra as na; use raylib::prelude::*; +use crate::asset_manager::load_texture_from_internal_data; + #[derive(Debug)] pub struct Player { pub position: na::Vector2, pub velocity: na::Vector2, pub size: f32, pub active_texture: i32, - pub textures: [String; 3], + pub textures: Vec, } impl Player { - /// Construct a new player. - pub fn new(position: na::Vector2) -> Self { + pub fn new( + raylib_handle: &mut raylib::RaylibHandle, + thread: &raylib::RaylibThread, + position: na::Vector2, + ) -> 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, - active_texture: 0, - textures: [ - "assets/chr/chr_cubee/chr_cubeeLarge.png".to_string(), - "assets/chr/chr_cubee/chr_cubeeMedium.png".to_string(), - "assets/chr/chr_cubee/chr_cubeeSmall.png".to_string() - ] + size: 1.0, + active_texture: 0, + textures, } } - } diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index 6c3f41c3..8d6802c4 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -60,11 +60,18 @@ 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 }, @@ -347,7 +354,7 @@ impl PlayableScene { player.velocity.y = 0.0; } - player.size -= 0.001; + player.size -= 0.001; self.update_camera(raylib); } From f48e5c11b958499c8e44bba40123aa5469d835cc Mon Sep 17 00:00:00 2001 From: Si Bartha Date: Sun, 3 Apr 2022 14:39:14 -0400 Subject: [PATCH 07/17] Shrinkage --- game/game_logic/src/scenes/player_interaction.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index 8d6802c4..c54c06d5 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -356,6 +356,8 @@ impl PlayableScene { player.size -= 0.001; + player.size -= 0.001; + self.update_camera(raylib); } From 46830e0f9384459777dcb84d06ec830fb47f2330 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sun, 3 Apr 2022 14:43:37 -0400 Subject: [PATCH 08/17] Load textures --- game/game_logic/src/scenes/player_interaction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index c54c06d5..ed8c8a40 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -60,7 +60,7 @@ impl PlayableScene { let game_soundtrack = load_music_from_internal_data(thread, "assets/audio/gameSoundtrack.mp3").unwrap(); - // Load the player + // Load the player let player = Player::new( raylib_handle, thread, @@ -356,7 +356,7 @@ impl PlayableScene { player.size -= 0.001; - player.size -= 0.001; + player.size -= 0.001; self.update_camera(raylib); } From bd713c3e751f25d1e5ff0e37696ae423ab7a6e67 Mon Sep 17 00:00:00 2001 From: Si Bartha Date: Sun, 3 Apr 2022 22:59:51 -0400 Subject: [PATCH 09/17] Merge Stash --- game/dist/project-constants.json | 3 +- game/game_logic/src/project_constants.rs | 3 ++ .../src/scenes/player_interaction.rs | 43 ++++++++++++++----- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/game/dist/project-constants.json b/game/dist/project-constants.json index 46f8c02e..ec52701f 100644 --- a/game/dist/project-constants.json +++ b/game/dist/project-constants.json @@ -21,6 +21,7 @@ "max_velocity": 3, "acceleration": 2, "deceleration": 1, - "start_size": 0.8 + "start_size": 0.8, + "melt_speed": 0.00944443 } } diff --git a/game/game_logic/src/project_constants.rs b/game/game_logic/src/project_constants.rs index 6df497d3..dde085bb 100644 --- a/game/game_logic/src/project_constants.rs +++ b/game/game_logic/src/project_constants.rs @@ -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 diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index ed8c8a40..b3c03a4d 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -78,7 +78,7 @@ impl PlayableScene { rotation: 0.0, zoom: 1.0, }, - last_update: SystemTime::UNIX_EPOCH, + last_update: SystemTime::now(), game_soundtrack, world_colliders, show_debug_info: false, @@ -134,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 @@ -188,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 = 1.0; + 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; @@ -231,6 +244,12 @@ impl PlayableScene { // 32, // Color::BLACK, // ); + let melt_amount = constants.player.melt_speed * (current_temperature_val)/(-247.51879); + + draw.draw_text( + format!("Funny Temperature: ({})[{}]", current_temperature_val, melt_amount).as_str(), + 10, 10, 20, Color::PAPAYAWHIP + ); } // Physics @@ -249,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 @@ -354,10 +373,14 @@ impl PlayableScene { player.velocity.y = 0.0; } - player.size -= 0.001; - - player.size -= 0.001; + 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 * (-247.51879)/(current_temperature_val); + player.size -= melt_amount * delta_time; + self.update_camera(raylib); } From 00fa6edb20beeb2875551a39ba217e92dcda18bc Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sun, 3 Apr 2022 23:03:44 -0400 Subject: [PATCH 10/17] Fix render order --- .../src/rendering/utilities/map_render.rs | 321 +++++++++--------- 1 file changed, 169 insertions(+), 152 deletions(-) diff --git a/game/game_logic/src/rendering/utilities/map_render.rs b/game/game_logic/src/rendering/utilities/map_render.rs index 93708e60..5fe9f045 100644 --- a/game/game_logic/src/rendering/utilities/map_render.rs +++ b/game/game_logic/src/rendering/utilities/map_render.rs @@ -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,154 +330,6 @@ impl MapRenderer { Color::WHITE, ); } - - // 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 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, - ); - } - } - } - } - - 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); - } } } } @@ -489,6 +340,172 @@ impl MapRenderer { tiled::LayerType::GroupLayer(_) => todo!(), } } + + // Keep track of our sampler X and Y values + let mut sampler_x = 0; + let mut sampler_y = 0; + + // 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; + + // If we are looking at a new tile, update the sampler + if sampler_y != tile_y { + sampler_y = tile_y; + + 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 we are looking at a new tile, update the sampler + if sampler_x != tile_x { + sampler_x = tile_x; + + // 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 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, + ); + } + } + } + } + + 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); + } + } + } + } + } } /// Get the list of world colliders From 064d14f97efb2a34334e28b94ea621f0fa579b50 Mon Sep 17 00:00:00 2001 From: Si Bartha Date: Sun, 3 Apr 2022 23:26:19 -0400 Subject: [PATCH 11/17] funny --- game/game_logic/src/scenes/player_interaction.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index b3c03a4d..9ef0c2d9 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -196,7 +196,7 @@ impl PlayableScene { 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 = 1.0; + let mut current_temperature_val: f32 = -247.51879; if let Some(val) = current_temperature { current_temperature_val = val; } @@ -244,7 +244,7 @@ impl PlayableScene { // 32, // Color::BLACK, // ); - let melt_amount = constants.player.melt_speed * (current_temperature_val)/(-247.51879); + let melt_amount = (current_temperature_val)/(-247.51879); draw.draw_text( format!("Funny Temperature: ({})[{}]", current_temperature_val, melt_amount).as_str(), @@ -378,7 +378,8 @@ impl PlayableScene { current_temperature_val = val - 273.15; } - let melt_amount = constants.player.melt_speed * (-247.51879)/(current_temperature_val); + let melt_amount = constants.player.melt_speed * (current_temperature_val)/(-247.51879); + player.size -= melt_amount * delta_time; self.update_camera(raylib); From f1d8ff3408b572f2ba6f8e9fd06456598f8461ed Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sun, 3 Apr 2022 23:33:42 -0400 Subject: [PATCH 12/17] Pre-render footprints --- .../src/model/world_object_package.rs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/game/game_logic/src/model/world_object_package.rs b/game/game_logic/src/model/world_object_package.rs index 38c45546..0f6064a9 100644 --- a/game/game_logic/src/model/world_object_package.rs +++ b/game/game_logic/src/model/world_object_package.rs @@ -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, /// A list of colliders in the world. We pre-solve these to make comput happy :) pub world_space_colliders: Vec, + /// A list of footprints in the world. We pre-solve these to make comput happy :) + pub world_space_footprints: Vec, } 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 = Vec::new(); + let mut world_space_footprints: Vec = 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, }) } } From 3b1b9c79ce63ac71a6de03133f8f2db100e668b3 Mon Sep 17 00:00:00 2001 From: Milu Date: Sun, 3 Apr 2022 23:47:40 -0400 Subject: [PATCH 13/17] intro cutscene comic --- .../assets/cut/cut_start/cut_introScene.png | Bin 0 -> 55260 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 game/dist/assets/cut/cut_start/cut_introScene.png diff --git a/game/dist/assets/cut/cut_start/cut_introScene.png b/game/dist/assets/cut/cut_start/cut_introScene.png new file mode 100644 index 0000000000000000000000000000000000000000..51a49e5e70ff7380b88ba11f514e329bde8f1640 GIT binary patch literal 55260 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 Date: Mon, 4 Apr 2022 00:07:07 -0400 Subject: [PATCH 14/17] Arty art --- .../cut_intro.png} | Bin .../{cut_youMelted.png => cut_melty.png} | Bin game/game_logic/src/scenes/cutscenes.rs | 126 +++++++++++++++--- 3 files changed, 111 insertions(+), 15 deletions(-) rename game/dist/assets/cut/{cut_start/cut_introScene.png => cut_intro/cut_intro.png} (100%) rename game/dist/assets/cut/cut_melty/{cut_youMelted.png => cut_melty.png} (100%) diff --git a/game/dist/assets/cut/cut_start/cut_introScene.png b/game/dist/assets/cut/cut_intro/cut_intro.png similarity index 100% rename from game/dist/assets/cut/cut_start/cut_introScene.png rename to game/dist/assets/cut/cut_intro/cut_intro.png diff --git a/game/dist/assets/cut/cut_melty/cut_youMelted.png b/game/dist/assets/cut/cut_melty/cut_melty.png similarity index 100% rename from game/dist/assets/cut/cut_melty/cut_youMelted.png rename to game/dist/assets/cut/cut_melty/cut_melty.png diff --git a/game/game_logic/src/scenes/cutscenes.rs b/game/game_logic/src/scenes/cutscenes.rs index d5effa76..dc4fd78d 100644 --- a/game/game_logic/src/scenes/cutscenes.rs +++ b/game/game_logic/src/scenes/cutscenes.rs @@ -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(); From 1b32c57447a33279d2c16c05a49cc9e7867c4efd Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Mon, 4 Apr 2022 00:11:43 -0400 Subject: [PATCH 15/17] Possibly fix volume --- game/game_logic/src/rendering/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/game_logic/src/rendering/event_loop.rs b/game/game_logic/src/rendering/event_loop.rs index 4d94253c..a62d6177 100644 --- a/game/game_logic/src/rendering/event_loop.rs +++ b/game/game_logic/src/rendering/event_loop.rs @@ -50,7 +50,7 @@ pub async fn handle_graphics_blocking( // 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(); From e1b6311025f4e1ced5f864b568d3b21b0555fa16 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Mon, 4 Apr 2022 00:13:55 -0400 Subject: [PATCH 16/17] Give the game a name --- README.md | 2 +- game/dist/project-constants.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1c680199..274cec2f 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/game/dist/project-constants.json b/game/dist/project-constants.json index ec52701f..ca4d1628 100644 --- a/game/dist/project-constants.json +++ b/game/dist/project-constants.json @@ -1,5 +1,5 @@ { - "game_name": "Ludum Dare 50", + "game_name": "Melting Point", "base_window_size": [ 1080, 720 From c44b9191f3b37e2f4d83c7c7cc3027a0973f0ee5 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Mon, 4 Apr 2022 00:19:24 -0400 Subject: [PATCH 17/17] Discord RPC --- game/game_logic/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/game/game_logic/src/lib.rs b/game/game_logic/src/lib.rs index 09288dce..21cda248 100644 --- a/game/game_logic/src/lib.rs +++ b/game/game_logic/src/lib.rs @@ -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(