From ffe4d7f824bfc23003e19d5145f96dd5182cee95 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 15:27:00 -0400 Subject: [PATCH 01/32] base --- src/entities/enemy/base.rs | 7 +++++++ src/entities/enemy/mod.rs | 1 + src/entities/mod.rs | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/entities/enemy/base.rs create mode 100644 src/entities/enemy/mod.rs diff --git a/src/entities/enemy/base.rs b/src/entities/enemy/base.rs new file mode 100644 index 0000000..c330af7 --- /dev/null +++ b/src/entities/enemy/base.rs @@ -0,0 +1,7 @@ + + +pub trait EnemyBase { + fn render(); + fn handle_logic(); + fn handle_getting_attacked(); +} \ No newline at end of file diff --git a/src/entities/enemy/mod.rs b/src/entities/enemy/mod.rs new file mode 100644 index 0000000..307b359 --- /dev/null +++ b/src/entities/enemy/mod.rs @@ -0,0 +1 @@ +pub mod base; \ No newline at end of file diff --git a/src/entities/mod.rs b/src/entities/mod.rs index e948307..5bf6e50 100644 --- a/src/entities/mod.rs +++ b/src/entities/mod.rs @@ -1 +1,2 @@ -pub mod fish; \ No newline at end of file +pub mod fish; +pub mod enemy; \ No newline at end of file From e6726916a672ca666af3e66b7812e79a9ee1fa7b Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 15:40:17 -0400 Subject: [PATCH 02/32] extracting player code --- src/gamecore.rs | 9 ++-- src/logic/ingame/hud.rs | 8 +-- src/logic/ingame/mod.rs | 3 +- src/logic/ingame/playerlogic.rs | 57 --------------------- src/player.rs | 90 ++++++++++++++++++++++++++++++++- 5 files changed, 96 insertions(+), 71 deletions(-) diff --git a/src/gamecore.rs b/src/gamecore.rs index d8216b7..db20954 100644 --- a/src/gamecore.rs +++ b/src/gamecore.rs @@ -31,10 +31,10 @@ impl fmt::Display for GameState { #[derive(Debug, Serialize, Deserialize, Default)] pub struct GameProgress { - coins: u32, - max_depth: f32, - fastest_time: Option, - inventory: Vec, + pub coins: u32, + pub max_depth: f32, + pub fastest_time: Option, + pub inventory: Vec, } impl GameProgress { @@ -43,6 +43,7 @@ impl GameProgress { ..Default::default() } } + pub fn from_file(file: String) -> Result { // Load the file diff --git a/src/logic/ingame/hud.rs b/src/logic/ingame/hud.rs index 6a70ff9..e7e508d 100644 --- a/src/logic/ingame/hud.rs +++ b/src/logic/ingame/hud.rs @@ -8,13 +8,7 @@ pub fn render_hud( window_center: Vector2, ) { // Get the relevant data - let dist_from_player_to_end = game_core - .player - .position - .distance_to(game_core.world.end_position); - let dist_from_start_to_end = Vector2::zero().distance_to(game_core.world.end_position); - let progress = ((dist_from_start_to_end - dist_from_player_to_end) / dist_from_start_to_end) - .clamp(0.0, 1.0); + let progress = game_core.player.calculate_depth_percent(&game_core.world); // Determine the progress slider position let slider_bound_height = 20.0; diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index 18ab910..e58c92e 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -126,7 +126,8 @@ impl Screen for InGameScreen { } // Render Player - playerlogic::render_player(&mut context_2d, game_core); + // playerlogic::render_player(&mut context_2d, game_core); + game_core.player.render(&mut context_2d, &mut game_core.resources); } // Render the hud diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index 6446ec5..c1e5ec6 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -169,60 +169,3 @@ pub fn update_player_movement( // } } -pub fn render_player(context_2d: &mut RaylibMode2D, game_core: &mut GameCore) { - // Get the player - let player = &game_core.player; - - // Convert the player direction to a rotation - let player_rotation = Vector2::zero().angle_to(player.direction); - - // Render the player's boost ring - // This functions both as a breath meter, and as a boost meter - let boost_ring_max_radius = player.size.x + 5.0; - context_2d.draw_circle( - player.position.x as i32, - player.position.y as i32, - boost_ring_max_radius * player.boost_percent, - TRANSLUCENT_WHITE_64, - ); - context_2d.draw_ring( - Vector2 { - x: player.position.x as i32 as f32, - y: player.position.y as i32 as f32, - }, - boost_ring_max_radius, - boost_ring_max_radius + 1.0, - 0, - (360.0 * player.breath_percent) as i32, - 0, - TRANSLUCENT_WHITE_96, - ); - - // Render the player based on what is happening - if player.is_boost_charging { - game_core.resources.player_animation_boost_charge.draw( - context_2d, - player.position, - player_rotation.to_degrees() - 90.0, - ); - } else if player.is_boosting { - game_core.resources.player_animation_boost.draw( - context_2d, - player.position, - player_rotation.to_degrees() - 90.0, - ); - } else if player.is_moving { - game_core.resources.player_animation_regular.draw( - context_2d, - player.position, - player_rotation.to_degrees() - 90.0, - ); - } else { - game_core.resources.player_animation_regular.draw_frame( - context_2d, - player.position, - player_rotation.to_degrees() - 90.0, - 0, - ); - } -} diff --git a/src/player.rs b/src/player.rs index 01c5343..6daddf1 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,6 +1,13 @@ -use raylib::math::{Rectangle, Vector2}; +use raylib::prelude::*; -use crate::lib::utils::triangles::rotate_vector; +use crate::{ + gamecore::{GameCore, GameProgress}, + items::ShopItems, + lib::utils::triangles::rotate_vector, + pallette::{TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, + resources::GlobalResources, + world::World, +}; #[derive(Debug, Default)] pub struct Player { @@ -13,6 +20,7 @@ pub struct Player { pub is_moving: bool, pub is_boosting: bool, pub is_boost_charging: bool, + pub inventory: Vec, } impl Player { @@ -56,4 +64,82 @@ impl Player { return rectangle.check_collision_circle_rec(self.position, (self.size.y * 0.5) / 2.0); } + + /// Calculate how far the player is + pub fn calculate_depth_percent(&self, world: &World) -> f32 { + let dist_from_player_to_end = self.position.distance_to(world.end_position); + let dist_from_start_to_end = Vector2::zero().distance_to(world.end_position); + return ((dist_from_start_to_end - dist_from_player_to_end) / dist_from_start_to_end) + .clamp(0.0, 1.0); + } + + /// Create GameProgress from the current life + pub fn create_statistics(&self, game_core: &GameCore, current_time: f64) -> GameProgress { + GameProgress { + coins: self.coins, + inventory: self.inventory.clone(), + max_depth: self.calculate_depth_percent(&game_core.world), + fastest_time: Some(current_time - game_core.last_state_change_time), + } + } + + /// Render the player + pub fn render( + &self, + context_2d: &mut RaylibMode2D, + resources: &mut GlobalResources, + ) { + // Convert the player direction to a rotation + let player_rotation = Vector2::zero().angle_to(self.direction); + + // Render the player's boost ring + // This functions both as a breath meter, and as a boost meter + let boost_ring_max_radius = self.size.x + 5.0; + context_2d.draw_circle( + self.position.x as i32, + self.position.y as i32, + boost_ring_max_radius * self.boost_percent, + TRANSLUCENT_WHITE_64, + ); + context_2d.draw_ring( + Vector2 { + x: self.position.x as i32 as f32, + y: self.position.y as i32 as f32, + }, + boost_ring_max_radius, + boost_ring_max_radius + 1.0, + 0, + (360.0 * self.breath_percent) as i32, + 0, + TRANSLUCENT_WHITE_96, + ); + + // Render the player based on what is happening + if self.is_boost_charging { + resources.player_animation_boost_charge.draw( + context_2d, + self.position, + player_rotation.to_degrees() - 90.0, + ); + } else if self.is_boosting { + resources.player_animation_boost.draw( + context_2d, + self.position, + player_rotation.to_degrees() - 90.0, + ); + } else if self.is_moving { + resources.player_animation_regular.draw( + context_2d, + self.position, + player_rotation.to_degrees() - 90.0, + ); + } else { + resources.player_animation_regular.draw_frame( + context_2d, + self.position, + player_rotation.to_degrees() - 90.0, + 0, + ); + } + } } From 7d06fcb6d1a3dfa20e72c0422158cf271fd566d9 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 15:46:28 -0400 Subject: [PATCH 03/32] more cleanup --- src/lib/utils/profiler.rs | 18 +++++++++++++++++- src/main.rs | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/utils/profiler.rs b/src/lib/utils/profiler.rs index c813885..e8af3ea 100644 --- a/src/lib/utils/profiler.rs +++ b/src/lib/utils/profiler.rs @@ -1,3 +1,4 @@ +use raylib::math::Vector2; use serde_json::json; use serialstudio::{ data::{DataGroup, DataSet, TelemetryFrame}, @@ -21,7 +22,8 @@ pub struct ProfilerData { // Player pub player_coins: u32, pub player_boost_percent: f32, - pub player_breath_percent: f32 + pub player_breath_percent: f32, + pub player_pose: Vector2 } /// The development profiler @@ -147,6 +149,20 @@ impl GameProfiler { unit: Some("%".to_string()), w_type: None, }, + DataSet { + title: Some("X".to_string()), + value: json!(self.data.player_pose.x), + graph: Some(false), + unit: Some("pixels".to_string()), + w_type: None, + }, + DataSet { + title: Some("Y".to_string()), + value: json!(self.data.player_pose.y), + graph: Some(false), + unit: Some("pixels".to_string()), + w_type: None, + }, ], }, ], diff --git a/src/main.rs b/src/main.rs index 75a8769..aa48d4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -134,6 +134,7 @@ fn main() { profiler.data.player_coins = game_core.player.coins; profiler.data.player_boost_percent = game_core.player.boost_percent; profiler.data.player_breath_percent = game_core.player.breath_percent; + profiler.data.player_pose = game_core.player.position; // Send telemetry data profiler.update(); From b51de3b81553e5986df32fc5a202fde12773e696 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 16:03:07 -0400 Subject: [PATCH 04/32] working on aoe --- src/items.rs | 10 +++++----- src/logic/ingame/playerlogic.rs | 8 +++++++- src/player.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/items.rs b/src/items.rs index b49d49b..f1559bb 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1,10 +1,10 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] #[serde(tag = "t", content = "c")] pub enum ShopItems { - StunGun(u8), + StunGun, AirBag, - Flashlight(u8), - Flippers(u8) -} \ No newline at end of file + Flashlight { power: u8 }, + Flippers { speed_increase: u8 }, +} diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index c1e5ec6..2bf695e 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -128,7 +128,8 @@ pub fn update_player_movement( // Only do this if the mouse is far enough away let player_real_movement = game_core.player.direction * speed_multiplier; - if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 { + let player_stunned = game_core.player.stun_timer > 0.0; + if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 || player_stunned{ game_core.player.is_moving = true; game_core.player.position += player_real_movement; @@ -145,6 +146,11 @@ pub fn update_player_movement( } } else { game_core.player.is_moving = false; + + // Handle updating the stun timer + if player_stunned { + game_core.player.stun_timer -= dt; + } } // Move the camera to follow the player diff --git a/src/player.rs b/src/player.rs index 6daddf1..f7850ab 100644 --- a/src/player.rs +++ b/src/player.rs @@ -21,6 +21,8 @@ pub struct Player { pub is_boosting: bool, pub is_boost_charging: bool, pub inventory: Vec, + pub stun_timer: f64, + pub attacking_timer: f64, } impl Player { @@ -65,6 +67,18 @@ impl Player { return rectangle.check_collision_circle_rec(self.position, (self.size.y * 0.5) / 2.0); } + /// Stun the player + pub fn set_stun_seconds(&mut self, seconds: f64) { + self.stun_timer = seconds; + } + + /// Try to attack with the stun gun + pub fn begin_attack(&mut self) { + if self.inventory.contains(&ShopItems::StunGun) && self.stun_timer == 0.0 { + self.stun_timer = 2.0; + } + } + /// Calculate how far the player is pub fn calculate_depth_percent(&self, world: &World) -> f32 { let dist_from_player_to_end = self.position.distance_to(world.end_position); @@ -114,6 +128,20 @@ impl Player { TRANSLUCENT_WHITE_96, ); + // Calculate AOE ring + let aoe_ring; + if self.attacking_timer <= 0.25 { + aoe_ring = self.attacking_timer; + } + + // Render attack AOE + context_2d.draw_circle_lines( + self.position.x as i32, + self.position.y as i32, + boost_ring_max_radius * self.boost_percent, + TRANSLUCENT_WHITE_64, + ); + // Render the player based on what is happening if self.is_boost_charging { resources.player_animation_boost_charge.draw( From 22e9eda97c7f47b37fd769fed3cbb5cb1c262894 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 16:21:48 -0400 Subject: [PATCH 05/32] aoe ring math --- src/lib/utils/mod.rs | 12 +++++++++++- src/logic/ingame/mod.rs | 2 +- src/logic/ingame/playerlogic.rs | 4 ++++ src/logic/loadingscreen.rs | 14 ++------------ src/player.rs | 24 +++++++++--------------- 5 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/lib/utils/mod.rs b/src/lib/utils/mod.rs index 9d6dd04..baba8af 100644 --- a/src/lib/utils/mod.rs +++ b/src/lib/utils/mod.rs @@ -1,2 +1,12 @@ pub mod profiler; -pub mod triangles; \ No newline at end of file +pub mod triangles; + +pub fn calculate_linear_slide(playthrough_percent: f64) -> f64 { + if playthrough_percent < 0.25 { + return playthrough_percent / 0.25; + } else if playthrough_percent > 0.75 { + return 1.0 - ((playthrough_percent - 0.75) / 0.25); + } else { + return 1.0; + } +} \ No newline at end of file diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index e58c92e..50fe9d7 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -127,7 +127,7 @@ impl Screen for InGameScreen { // Render Player // playerlogic::render_player(&mut context_2d, game_core); - game_core.player.render(&mut context_2d, &mut game_core.resources); + game_core.player.render(&mut context_2d, &mut game_core.resources, dt); } // Render the hud diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index 2bf695e..afd21f5 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -76,6 +76,10 @@ pub fn update_player_movement( let user_request_boost = draw_handle.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON); let user_request_action = draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON); + if user_request_action { + game_core.player.begin_attack(); + } + // Move the player in their direction let speed_multiplier; if user_request_boost && game_core.player.boost_percent >= 0.0 { diff --git a/src/logic/loadingscreen.rs b/src/logic/loadingscreen.rs index 184322b..06056c1 100644 --- a/src/logic/loadingscreen.rs +++ b/src/logic/loadingscreen.rs @@ -1,9 +1,6 @@ use raylib::prelude::*; -use crate::{ - gamecore::{GameCore, GameState}, - lib::wrappers::audio::player::AudioPlayer, -}; +use crate::{gamecore::{GameCore, GameState}, lib::{utils::calculate_linear_slide, wrappers::audio::player::AudioPlayer}}; use super::screen::Screen; @@ -32,14 +29,7 @@ impl LoadingScreen { fn get_logo_mask(&self, playthrough_percent: f64) -> Color { // Determine the alpha - let alpha; - if playthrough_percent < 0.25 { - alpha = playthrough_percent / 0.25 - } else if playthrough_percent > 0.75 { - alpha = 1.0 - ((playthrough_percent - 0.75) / 0.25); - } else { - alpha = 1.0; - } + let alpha = calculate_linear_slide(playthrough_percent); // Build a color mask Color { diff --git a/src/player.rs b/src/player.rs index f7850ab..e284f9e 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,13 +1,8 @@ use raylib::prelude::*; -use crate::{ - gamecore::{GameCore, GameProgress}, - items::ShopItems, - lib::utils::triangles::rotate_vector, - pallette::{TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, - resources::GlobalResources, - world::World, -}; +use crate::{gamecore::{GameCore, GameProgress}, items::ShopItems, lib::utils::{calculate_linear_slide, triangles::rotate_vector}, pallette::{TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, resources::GlobalResources, world::World}; + +const AOE_RING_MAX_RADIUS: f32 = 40.0; #[derive(Debug, Default)] pub struct Player { @@ -74,7 +69,7 @@ impl Player { /// Try to attack with the stun gun pub fn begin_attack(&mut self) { - if self.inventory.contains(&ShopItems::StunGun) && self.stun_timer == 0.0 { + if true || self.inventory.contains(&ShopItems::StunGun) && self.stun_timer == 0.0 { self.stun_timer = 2.0; } } @@ -99,9 +94,10 @@ impl Player { /// Render the player pub fn render( - &self, + &mut self, context_2d: &mut RaylibMode2D, resources: &mut GlobalResources, + dt: f64 ) { // Convert the player direction to a rotation let player_rotation = Vector2::zero().angle_to(self.direction); @@ -129,16 +125,14 @@ impl Player { ); // Calculate AOE ring - let aoe_ring; - if self.attacking_timer <= 0.25 { - aoe_ring = self.attacking_timer; - } + let aoe_ring = calculate_linear_slide(self.attacking_timer) as f32; + self.stun_timer = (self.stun_timer - dt).max(0.0); // Render attack AOE context_2d.draw_circle_lines( self.position.x as i32, self.position.y as i32, - boost_ring_max_radius * self.boost_percent, + AOE_RING_MAX_RADIUS * aoe_ring, TRANSLUCENT_WHITE_64, ); From 8c142d2711d10e9111a74b1e9c80d853652258cc Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 16:37:58 -0400 Subject: [PATCH 06/32] Rewrite the inventory --- src/gamecore.rs | 9 ++++++--- src/items.rs | 30 ++++++++++++++++++++++++------ src/player.rs | 41 ++++++++++++++++++++++++++--------------- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/gamecore.rs b/src/gamecore.rs index db20954..19b9592 100644 --- a/src/gamecore.rs +++ b/src/gamecore.rs @@ -6,7 +6,11 @@ use raylib::{ camera::Camera2D, math::Vector2, prelude::RaylibDrawHandle, RaylibHandle, RaylibThread, }; -use crate::{items::ShopItems, player::Player, resources::GlobalResources, world::World}; +use crate::{ + player::{Player, PlayerInventory}, + resources::GlobalResources, + world::World, +}; use failure::Error; use log::debug; @@ -34,7 +38,7 @@ pub struct GameProgress { pub coins: u32, pub max_depth: f32, pub fastest_time: Option, - pub inventory: Vec, + pub inventory: PlayerInventory, } impl GameProgress { @@ -43,7 +47,6 @@ impl GameProgress { ..Default::default() } } - pub fn from_file(file: String) -> Result { // Load the file diff --git a/src/items.rs b/src/items.rs index f1559bb..87d979e 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1,10 +1,28 @@ use serde::{Deserialize, Serialize}; +// #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +// #[serde(tag = "t", content = "c")] +// pub enum ShopItems { +// StunGun, +// AirBag, +// Flashlight { power: u8 }, +// Flippers { speed_increase: u8 }, +// } + + #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -#[serde(tag = "t", content = "c")] -pub enum ShopItems { - StunGun, - AirBag, - Flashlight { power: u8 }, - Flippers { speed_increase: u8 }, +pub struct StunGun { + range: f32, + duration: f64 } + +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub struct AirBag; + +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub struct Flashlight; + +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub struct Flippers { + speed_increase: f32 +} \ No newline at end of file diff --git a/src/player.rs b/src/player.rs index e284f9e..afdd68c 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,8 +1,17 @@ use raylib::prelude::*; +use serde::{Serialize, Deserialize}; +use crate::{gamecore::{GameCore, GameProgress}, items::{AirBag, Flashlight, Flippers, StunGun}, lib::utils::{calculate_linear_slide}, pallette::{TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, resources::GlobalResources, world::World}; -use crate::{gamecore::{GameCore, GameProgress}, items::ShopItems, lib::utils::{calculate_linear_slide, triangles::rotate_vector}, pallette::{TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, resources::GlobalResources, world::World}; +const AOE_RING_MAX_RADIUS: f32 = 60.0; +const STUN_ATTACK_TIME: f64 = 0.75; -const AOE_RING_MAX_RADIUS: f32 = 40.0; +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +pub struct PlayerInventory { + stun_gun: Option, + air_bag: Option, + flashlight: Option, + flippers: Option +} #[derive(Debug, Default)] pub struct Player { @@ -15,7 +24,7 @@ pub struct Player { pub is_moving: bool, pub is_boosting: bool, pub is_boost_charging: bool, - pub inventory: Vec, + pub inventory: PlayerInventory, pub stun_timer: f64, pub attacking_timer: f64, } @@ -69,8 +78,8 @@ impl Player { /// Try to attack with the stun gun pub fn begin_attack(&mut self) { - if true || self.inventory.contains(&ShopItems::StunGun) && self.stun_timer == 0.0 { - self.stun_timer = 2.0; + if true || self.inventory.stun_gun.is_some() && self.stun_timer == 0.0 { + self.attacking_timer = STUN_ATTACK_TIME; } } @@ -97,7 +106,7 @@ impl Player { &mut self, context_2d: &mut RaylibMode2D, resources: &mut GlobalResources, - dt: f64 + dt: f64, ) { // Convert the player direction to a rotation let player_rotation = Vector2::zero().angle_to(self.direction); @@ -125,16 +134,18 @@ impl Player { ); // Calculate AOE ring - let aoe_ring = calculate_linear_slide(self.attacking_timer) as f32; - self.stun_timer = (self.stun_timer - dt).max(0.0); + if self.attacking_timer != 0.0 { + let aoe_ring = calculate_linear_slide( self.attacking_timer / STUN_ATTACK_TIME) as f32; + self.attacking_timer = (self.attacking_timer - dt).max(0.0); - // Render attack AOE - context_2d.draw_circle_lines( - self.position.x as i32, - self.position.y as i32, - AOE_RING_MAX_RADIUS * aoe_ring, - TRANSLUCENT_WHITE_64, - ); + // Render attack AOE + context_2d.draw_circle_lines( + self.position.x as i32, + self.position.y as i32, + AOE_RING_MAX_RADIUS * aoe_ring, + TRANSLUCENT_WHITE_64, + ); + } // Render the player based on what is happening if self.is_boost_charging { From 43eab974b07e29658c68a1844ac0ac6ecfb97b47 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 16:39:36 -0400 Subject: [PATCH 07/32] fix access --- src/items.rs | 6 +++--- src/player.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/items.rs b/src/items.rs index 87d979e..a541098 100644 --- a/src/items.rs +++ b/src/items.rs @@ -12,8 +12,8 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct StunGun { - range: f32, - duration: f64 + pub range: f32, + pub duration: f64 } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] @@ -24,5 +24,5 @@ pub struct Flashlight; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Flippers { - speed_increase: f32 + pub speed_increase: f32 } \ No newline at end of file diff --git a/src/player.rs b/src/player.rs index afdd68c..6562a32 100644 --- a/src/player.rs +++ b/src/player.rs @@ -79,7 +79,7 @@ impl Player { /// Try to attack with the stun gun pub fn begin_attack(&mut self) { if true || self.inventory.stun_gun.is_some() && self.stun_timer == 0.0 { - self.attacking_timer = STUN_ATTACK_TIME; + self.attacking_timer = self.inventory.stun_gun.as_ref().unwrap().duration; } } From 8db5ff65ec8ba9f4c59097a48cd464cdf6f729a6 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 16:46:06 -0400 Subject: [PATCH 08/32] flipper logic --- src/items.rs | 44 +++++++++++++++++++++++---------- src/logic/ingame/playerlogic.rs | 5 ++++ src/player.rs | 16 ++++++------ 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/items.rs b/src/items.rs index a541098..60d99a3 100644 --- a/src/items.rs +++ b/src/items.rs @@ -1,19 +1,24 @@ use serde::{Deserialize, Serialize}; -// #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -// #[serde(tag = "t", content = "c")] -// pub enum ShopItems { -// StunGun, -// AirBag, -// Flashlight { power: u8 }, -// Flippers { speed_increase: u8 }, -// } - - #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct StunGun { pub range: f32, - pub duration: f64 + pub duration: f64, +} + +impl StunGun { + pub fn lvl1() -> Self { + Self { + range: 30.0, + duration: 0.5, + } + } + pub fn lvl2() -> Self { + Self { + range: 60.0, + duration: 0.75, + } + } } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] @@ -24,5 +29,18 @@ pub struct Flashlight; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Flippers { - pub speed_increase: f32 -} \ No newline at end of file + pub speed_increase: f32, +} + +impl Flippers { + pub fn lvl1() -> Self { + Self { + speed_increase: 1.2 + } + } + pub fn lvl2() -> Self { + Self { + speed_increase: 1.5 + } + } +} diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index afd21f5..5a1ace8 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -126,6 +126,11 @@ pub fn update_player_movement( } } + // Handle flippers doing a speed increase + if game_core.player.inventory.flippers.is_some() { + let speed_multiplier = speed_multiplier * game_core.player.inventory.flippers.as_ref().unwrap().speed_increase; + } + // Update the player's breath game_core.player.breath_percent = (game_core.player.breath_percent - BREATH_DECREASE_PER_SECOND * dt as f32).clamp(0.0, 1.0); diff --git a/src/player.rs b/src/player.rs index 6562a32..265686b 100644 --- a/src/player.rs +++ b/src/player.rs @@ -7,10 +7,10 @@ const STUN_ATTACK_TIME: f64 = 0.75; #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct PlayerInventory { - stun_gun: Option, - air_bag: Option, - flashlight: Option, - flippers: Option + pub stun_gun: Option, + pub air_bag: Option, + pub flashlight: Option, + pub flippers: Option } #[derive(Debug, Default)] @@ -78,7 +78,7 @@ impl Player { /// Try to attack with the stun gun pub fn begin_attack(&mut self) { - if true || self.inventory.stun_gun.is_some() && self.stun_timer == 0.0 { + if self.inventory.stun_gun.is_some() && self.stun_timer == 0.0 { self.attacking_timer = self.inventory.stun_gun.as_ref().unwrap().duration; } } @@ -134,15 +134,15 @@ impl Player { ); // Calculate AOE ring - if self.attacking_timer != 0.0 { - let aoe_ring = calculate_linear_slide( self.attacking_timer / STUN_ATTACK_TIME) as f32; + if self.attacking_timer != 0.0 && self.inventory.stun_gun.is_some() { + let aoe_ring = calculate_linear_slide( self.attacking_timer / self.inventory.stun_gun.as_ref().unwrap().duration) as f32; self.attacking_timer = (self.attacking_timer - dt).max(0.0); // Render attack AOE context_2d.draw_circle_lines( self.position.x as i32, self.position.y as i32, - AOE_RING_MAX_RADIUS * aoe_ring, + self.inventory.stun_gun.as_ref().unwrap().range * aoe_ring, TRANSLUCENT_WHITE_64, ); } From 296d5fff8f0c1abe433a313fdadcf181f8cc4777 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 16:48:16 -0400 Subject: [PATCH 09/32] checking for stun gun use --- src/logic/ingame/playerlogic.rs | 4 ++-- src/player.rs | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index 5a1ace8..86891ba 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -81,7 +81,7 @@ pub fn update_player_movement( } // Move the player in their direction - let speed_multiplier; + let mut speed_multiplier; if user_request_boost && game_core.player.boost_percent >= 0.0 { // Set the speed multiplier speed_multiplier = BOOST_PLAYER_SPEED as f32; @@ -128,7 +128,7 @@ pub fn update_player_movement( // Handle flippers doing a speed increase if game_core.player.inventory.flippers.is_some() { - let speed_multiplier = speed_multiplier * game_core.player.inventory.flippers.as_ref().unwrap().speed_increase; + speed_multiplier = speed_multiplier * game_core.player.inventory.flippers.as_ref().unwrap().speed_increase; } // Update the player's breath diff --git a/src/player.rs b/src/player.rs index 265686b..cb49896 100644 --- a/src/player.rs +++ b/src/player.rs @@ -83,6 +83,10 @@ impl Player { } } + pub fn is_stun_gun_active(&self) -> bool { + return self.attacking_timer != 0.0 && self.inventory.stun_gun.is_some(); + } + /// Calculate how far the player is pub fn calculate_depth_percent(&self, world: &World) -> f32 { let dist_from_player_to_end = self.position.distance_to(world.end_position); @@ -134,7 +138,7 @@ impl Player { ); // Calculate AOE ring - if self.attacking_timer != 0.0 && self.inventory.stun_gun.is_some() { + if self.is_stun_gun_active() { let aoe_ring = calculate_linear_slide( self.attacking_timer / self.inventory.stun_gun.as_ref().unwrap().duration) as f32; self.attacking_timer = (self.attacking_timer - dt).max(0.0); From a3d79a5ebc9f816df4b772bcbd569e053ede7109 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 17:02:37 -0400 Subject: [PATCH 10/32] jellyfish rendering --- assets/img/enemies/jelly.json | 66 ++++++++++++++++++++++++++++++++ assets/img/enemies/jelly.png | Bin 0 -> 354 bytes assets/worlds/mainworld.json | 8 ++++ src/entities/enemy/base.rs | 8 ++-- src/entities/enemy/jellyfish.rs | 33 ++++++++++++++++ src/entities/enemy/mod.rs | 3 +- src/logic/ingame/mod.rs | 10 ++--- src/world.rs | 6 ++- 8 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 assets/img/enemies/jelly.json create mode 100644 assets/img/enemies/jelly.png create mode 100644 src/entities/enemy/jellyfish.rs diff --git a/assets/img/enemies/jelly.json b/assets/img/enemies/jelly.json new file mode 100644 index 0000000..2f02023 --- /dev/null +++ b/assets/img/enemies/jelly.json @@ -0,0 +1,66 @@ +{ "frames": { + "Sprite-0001 0.": { + "frame": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 1.": { + "frame": { "x": 10, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 2.": { + "frame": { "x": 20, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 3.": { + "frame": { "x": 30, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 4.": { + "frame": { "x": 40, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 5.": { + "frame": { "x": 50, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + } + }, + "meta": { + "app": "http://www.aseprite.org/", + "version": "1.2.27-x64", + "image": "jelly.png", + "format": "RGBA8888", + "size": { "w": 60, "h": 10 }, + "scale": "1", + "frameTags": [ + ], + "layers": [ + { "name": "Layer 1", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/assets/img/enemies/jelly.png b/assets/img/enemies/jelly.png new file mode 100644 index 0000000000000000000000000000000000000000..8e670f2a30a7a8436e587bd332cdc851e126ee5e GIT binary patch literal 354 zcmV-o0iFJdP)Px$97#k$R7i={l(7ziFc5|dad08Vk+3;A5Js68op>Q0ffsUOWWrII*@$!F;^}J252GFi*q==pdJLv zsv9-&>3M#C@~9P&{UpVa>J5d^zh7a~)`;y}h>DPK6jyGwEqLI@Df zzhQ7VwZ`V=hJAcfzz~bv#W%mhvK$8lWH}D996y8{4jJV1+@-D>;tIKuvH9kIczVY3 zIxS>54%EeA<8a@^>1Vigk*A&jjxR)LP2kFPRo|L;A0Mu}s&9|?eX_=Ie8$Vkba4=u z7@3=#wsX`iPdR+%AUC;#*c#!Sw>61=mw_>V19Wju^>eqxApigX07*qoM6N<$g5V6D AsQ>@~ literal 0 HcmV?d00001 diff --git a/assets/worlds/mainworld.json b/assets/worlds/mainworld.json index 40d085e..c5c7288 100644 --- a/assets/worlds/mainworld.json +++ b/assets/worlds/mainworld.json @@ -9,5 +9,13 @@ }, "fish": [ {"x":49,"y":801},{"x":570,"y":594},{"x":761,"y":186},{"x":760,"y":940},{"x":241,"y":32},{"x":501,"y":18},{"x":487,"y":37},{"x":802,"y":849},{"x":864,"y":43},{"x":544,"y":886},{"x":987,"y":710},{"x":949,"y":404},{"x":694,"y":32},{"x":364,"y":899},{"x":26,"y":849},{"x":253,"y":627},{"x":39,"y":547},{"x":307,"y":730},{"x":133,"y":967},{"x":861,"y":76},{"x":199,"y":229},{"x":617,"y":532},{"x":391,"y":388},{"x":491,"y":816},{"x":539,"y":243},{"x":222,"y":288},{"x":81,"y":784},{"x":432,"y":830},{"x":741,"y":737},{"x":426,"y":480},{"x":591,"y":437},{"x":903,"y":380},{"x":653,"y":349},{"x":684,"y":235},{"x":797,"y":438},{"x":546,"y":615},{"x":497,"y":523},{"x":406,"y":468},{"x":173,"y":183},{"x":641,"y":187},{"x":517,"y":294},{"x":527,"y":650},{"x":962,"y":237},{"x":25,"y":868},{"x":16,"y":369},{"x":434,"y":712},{"x":632,"y":315},{"x":172,"y":421},{"x":450,"y":53},{"x":731,"y":220},{"x":532,"y":467},{"x":816,"y":497},{"x":948,"y":539},{"x":467,"y":829},{"x":533,"y":809},{"x":146,"y":989},{"x":850,"y":245},{"x":989,"y":214},{"x":203,"y":354},{"x":466,"y":611},{"x":382,"y":376},{"x":111,"y":148},{"x":411,"y":77},{"x":124,"y":418},{"x":154,"y":611},{"x":56,"y":732},{"x":800,"y":488},{"x":851,"y":668},{"x":240,"y":220},{"x":1000,"y":62},{"x":95,"y":784},{"x":700,"y":428},{"x":735,"y":517},{"x":259,"y":843},{"x":647,"y":268},{"x":668,"y":823},{"x":198,"y":241},{"x":243,"y":422},{"x":838,"y":433},{"x":642,"y":97},{"x":563,"y":974},{"x":386,"y":548},{"x":646,"y":482},{"x":691,"y":794},{"x":167,"y":485},{"x":978,"y":622},{"x":845,"y":431},{"x":529,"y":719},{"x":963,"y":145},{"x":6,"y":412},{"x":381,"y":830},{"x":918,"y":118},{"x":915,"y":27},{"x":618,"y":262},{"x":250,"y":635},{"x":100,"y":500},{"x":442,"y":321},{"x":769,"y":767},{"x":714,"y":204},{"x":506,"y":872},{"x":575,"y":178},{"x":256,"y":411},{"x":921,"y":617},{"x":971,"y":214},{"x":726,"y":702},{"x":103,"y":450},{"x":501,"y":134},{"x":265,"y":993},{"x":31,"y":63},{"x":502,"y":448},{"x":46,"y":457},{"x":809,"y":184},{"x":763,"y":962},{"x":632,"y":873},{"x":916,"y":761},{"x":710,"y":720},{"x":873,"y":222},{"x":256,"y":861},{"x":246,"y":482},{"x":390,"y":812},{"x":28,"y":247},{"x":516,"y":523},{"x":869,"y":43},{"x":680,"y":740},{"x":406,"y":65},{"x":657,"y":196},{"x":692,"y":635},{"x":97,"y":993},{"x":616,"y":490},{"x":515,"y":955},{"x":412,"y":502},{"x":743,"y":565},{"x":16,"y":499},{"x":324,"y":582},{"x":871,"y":62},{"x":128,"y":476},{"x":716,"y":525},{"x":627,"y":2},{"x":730,"y":913},{"x":704,"y":522},{"x":242,"y":934},{"x":172,"y":277},{"x":651,"y":948},{"x":349,"y":263},{"x":731,"y":967},{"x":382,"y":762},{"x":217,"y":15},{"x":49,"y":25},{"x":583,"y":110},{"x":700,"y":620},{"x":230,"y":537},{"x":285,"y":978},{"x":4,"y":791},{"x":939,"y":866},{"x":371,"y":342},{"x":759,"y":870},{"x":892,"y":103},{"x":57,"y":129},{"x":233,"y":383},{"x":171,"y":472},{"x":173,"y":842},{"x":516,"y":464},{"x":407,"y":458},{"x":963,"y":231},{"x":526,"y":253},{"x":815,"y":857},{"x":175,"y":909},{"x":993,"y":255},{"x":129,"y":390},{"x":76,"y":997},{"x":833,"y":174},{"x":501,"y":396},{"x":897,"y":218},{"x":876,"y":601},{"x":41,"y":165},{"x":993,"y":473},{"x":606,"y":308},{"x":831,"y":382},{"x":517,"y":828},{"x":984,"y":26},{"x":286,"y":712},{"x":422,"y":311},{"x":448,"y":103},{"x":260,"y":229},{"x":5,"y":738},{"x":283,"y":346},{"x":744,"y":463},{"x":634,"y":719},{"x":446,"y":977},{"x":220,"y":89},{"x":745,"y":866},{"x":851,"y":860},{"x":369,"y":940},{"x":828,"y":577},{"x":350,"y":337},{"x":334,"y":378},{"x":203,"y":248},{"x":665,"y":788},{"x":334,"y":927},{"x":307,"y":764},{"x":500,"y":763},{"x":613,"y":843},{"x":384,"y":253},{"x":956,"y":569},{"x":846,"y":137},{"x":105,"y":728},{"x":686,"y":226},{"x":657,"y":52},{"x":592,"y":433},{"x":997,"y":820},{"x":746,"y":389},{"x":405,"y":448},{"x":973,"y":19},{"x":538,"y":518},{"x":790,"y":275},{"x":633,"y":738},{"x":128,"y":484},{"x":603,"y":371},{"x":932,"y":21},{"x":582,"y":445},{"x":438,"y":793},{"x":963,"y":69},{"x":158,"y":263},{"x":988,"y":297},{"x":249,"y":227},{"x":245,"y":466},{"x":131,"y":495},{"x":620,"y":266},{"x":505,"y":384},{"x":813,"y":647},{"x":113,"y":66},{"x":757,"y":10},{"x":2,"y":707},{"x":540,"y":140},{"x":562,"y":691},{"x":484,"y":433},{"x":859,"y":455},{"x":248,"y":117},{"x":36,"y":432},{"x":798,"y":754},{"x":611,"y":291},{"x":664,"y":770},{"x":299,"y":788},{"x":433,"y":920},{"x":540,"y":739},{"x":201,"y":829},{"x":972,"y":362},{"x":811,"y":120},{"x":941,"y":670},{"x":186,"y":448},{"x":549,"y":611},{"x":206,"y":387},{"x":973,"y":437},{"x":700,"y":709},{"x":472,"y":243},{"x":971,"y":518},{"x":184,"y":540},{"x":271,"y":257},{"x":290,"y":895},{"x":546,"y":7},{"x":256,"y":542},{"x":418,"y":553},{"x":816,"y":875},{"x":908,"y":547},{"x":315,"y":354},{"x":266,"y":471},{"x":242,"y":88},{"x":785,"y":52},{"x":497,"y":47},{"x":466,"y":279},{"x":750,"y":690},{"x":329,"y":296},{"x":545,"y":715},{"x":508,"y":562},{"x":993,"y":467},{"x":703,"y":733},{"x":824,"y":11},{"x":419,"y":337},{"x":393,"y":229},{"x":898,"y":261},{"x":264,"y":708},{"x":711,"y":768},{"x":568,"y":409},{"x":473,"y":342},{"x":329,"y":53},{"x":95,"y":815},{"x":783,"y":977},{"x":48,"y":551},{"x":635,"y":931},{"x":653,"y":86},{"x":9,"y":153},{"x":955,"y":660},{"x":480,"y":716},{"x":936,"y":622},{"x":607,"y":221},{"x":423,"y":545},{"x":507,"y":668},{"x":676,"y":957},{"x":253,"y":515},{"x":327,"y":495},{"x":965,"y":808},{"x":2,"y":807},{"x":276,"y":199},{"x":584,"y":75},{"x":770,"y":51},{"x":667,"y":717},{"x":944,"y":913},{"x":982,"y":977},{"x":618,"y":482},{"x":372,"y":545},{"x":507,"y":518},{"x":604,"y":492},{"x":772,"y":730},{"x":350,"y":141},{"x":783,"y":437},{"x":282,"y":714},{"x":269,"y":691},{"x":991,"y":386},{"x":234,"y":196},{"x":908,"y":635},{"x":785,"y":340},{"x":125,"y":712},{"x":466,"y":210},{"x":280,"y":185},{"x":995,"y":466},{"x":589,"y":258},{"x":700,"y":120},{"x":855,"y":323},{"x":690,"y":355},{"x":755,"y":353},{"x":378,"y":970},{"x":865,"y":270},{"x":220,"y":62},{"x":685,"y":848},{"x":670,"y":907},{"x":710,"y":671},{"x":209,"y":68},{"x":642,"y":470},{"x":104,"y":642},{"x":631,"y":328},{"x":898,"y":424},{"x":909,"y":427},{"x":189,"y":141},{"x":259,"y":993},{"x":332,"y":791},{"x":842,"y":778},{"x":63,"y":390},{"x":146,"y":895},{"x":230,"y":274},{"x":316,"y":447},{"x":603,"y":59},{"x":377,"y":841},{"x":602,"y":119},{"x":728,"y":557},{"x":395,"y":514},{"x":379,"y":754},{"x":822,"y":840},{"x":860,"y":478},{"x":695,"y":360},{"x":156,"y":784},{"x":241,"y":353},{"x":195,"y":199},{"x":284,"y":110},{"x":484,"y":966},{"x":889,"y":370},{"x":246,"y":684},{"x":710,"y":345},{"x":382,"y":635},{"x":447,"y":948},{"x":741,"y":274},{"x":224,"y":883},{"x":99,"y":37},{"x":472,"y":803},{"x":141,"y":397},{"x":371,"y":602},{"x":7,"y":482},{"x":184,"y":990},{"x":555,"y":313},{"x":573,"y":886},{"x":167,"y":365},{"x":810,"y":721},{"x":958,"y":767},{"x":891,"y":561},{"x":314,"y":987},{"x":156,"y":95},{"x":349,"y":542},{"x":775,"y":35},{"x":121,"y":655},{"x":311,"y":242},{"x":534,"y":135},{"x":71,"y":134},{"x":367,"y":896},{"x":447,"y":524},{"x":120,"y":421},{"x":878,"y":398},{"x":469,"y":822},{"x":483,"y":966},{"x":240,"y":880},{"x":759,"y":980},{"x":531,"y":759},{"x":395,"y":118},{"x":354,"y":360},{"x":173,"y":924},{"x":550,"y":958},{"x":888,"y":379},{"x":244,"y":448},{"x":999,"y":554},{"x":941,"y":455},{"x":798,"y":916},{"x":134,"y":123},{"x":90,"y":440},{"x":923,"y":263},{"x":405,"y":595},{"x":194,"y":387},{"x":370,"y":697},{"x":943,"y":888},{"x":607,"y":336},{"x":168,"y":105},{"x":874,"y":66},{"x":675,"y":50},{"x":601,"y":242},{"x":925,"y":728},{"x":643,"y":609},{"x":769,"y":713},{"x":410,"y":913},{"x":153,"y":776},{"x":775,"y":949},{"x":184,"y":93},{"x":624,"y":632},{"x":899,"y":804},{"x":909,"y":327},{"x":371,"y":510},{"x":663,"y":415},{"x":337,"y":542},{"x":248,"y":104},{"x":925,"y":450},{"x":310,"y":925},{"x":4,"y":550},{"x":559,"y":652},{"x":671,"y":296},{"x":414,"y":60},{"x":972,"y":505},{"x":221,"y":147},{"x":318,"y":592},{"x":861,"y":656},{"x":258,"y":675},{"x":565,"y":390},{"x":703,"y":236},{"x":227,"y":76},{"x":989,"y":252},{"x":924,"y":419},{"x":983,"y":971},{"x":795,"y":244},{"x":256,"y":498},{"x":517,"y":674},{"x":89,"y":197},{"x":366,"y":234},{"x":41,"y":952},{"x":487,"y":981},{"x":939,"y":922},{"x":384,"y":315},{"x":958,"y":57},{"x":499,"y":152},{"x":716,"y":167},{"x":167,"y":301},{"x":781,"y":964},{"x":101,"y":215},{"x":605,"y":396},{"x":31,"y":973},{"x":128,"y":831},{"x":685,"y":701},{"x":150,"y":507},{"x":663,"y":77},{"x":792,"y":561},{"x":398,"y":281},{"x":168,"y":936},{"x":8,"y":266},{"x":19,"y":723},{"x":377,"y":975},{"x":68,"y":114},{"x":191,"y":784},{"x":94,"y":222},{"x":986,"y":578},{"x":474,"y":160},{"x":936,"y":945},{"x":603,"y":778},{"x":105,"y":845},{"x":955,"y":583},{"x":832,"y":905},{"x":264,"y":132},{"x":219,"y":747},{"x":515,"y":562},{"x":178,"y":198},{"x":999,"y":1},{"x":470,"y":345},{"x":450,"y":490},{"x":967,"y":306},{"x":257,"y":360},{"x":632,"y":26},{"x":916,"y":382},{"x":631,"y":194},{"x":492,"y":235},{"x":479,"y":373},{"x":887,"y":154},{"x":65,"y":181},{"x":956,"y":879},{"x":567,"y":578},{"x":718,"y":617},{"x":464,"y":243},{"x":545,"y":410},{"x":923,"y":340},{"x":978,"y":716},{"x":277,"y":261},{"x":462,"y":600},{"x":687,"y":507} + ], + "jellyfish": [ + { + "position": { + "x": 300, + "y": 100 + } + } ] } \ No newline at end of file diff --git a/src/entities/enemy/base.rs b/src/entities/enemy/base.rs index c330af7..04937e6 100644 --- a/src/entities/enemy/base.rs +++ b/src/entities/enemy/base.rs @@ -1,7 +1,9 @@ +use raylib::prelude::*; +use crate::player::Player; pub trait EnemyBase { - fn render(); - fn handle_logic(); - fn handle_getting_attacked(); + fn render(&self, context_2d: &mut RaylibMode2D); + fn handle_logic(&mut self, player: &mut Player, dt: f64); + fn handle_getting_attacked(&mut self); } \ No newline at end of file diff --git a/src/entities/enemy/jellyfish.rs b/src/entities/enemy/jellyfish.rs new file mode 100644 index 0000000..496387f --- /dev/null +++ b/src/entities/enemy/jellyfish.rs @@ -0,0 +1,33 @@ +use super::base::EnemyBase; +use raylib::prelude::*; +use serde::{Deserialize, Serialize}; +use crate::player::Player; + +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +pub struct JellyFish { + pub position: Vector2, + + #[serde(skip)] + pub stunned_timer: f64 +} + +impl JellyFish { + + +} + +impl EnemyBase for JellyFish { + fn render(&self, context_2d: &mut raylib::prelude::RaylibMode2D) { + + // TODO + context_2d.draw_circle_v(self.position, 5.0, Color::RED); + } + + fn handle_logic(&mut self, player: &mut Player, dt: f64) { + todo!() + } + + fn handle_getting_attacked(&mut self) { + todo!() + } +} \ No newline at end of file diff --git a/src/entities/enemy/mod.rs b/src/entities/enemy/mod.rs index 307b359..ea64b37 100644 --- a/src/entities/enemy/mod.rs +++ b/src/entities/enemy/mod.rs @@ -1 +1,2 @@ -pub mod base; \ No newline at end of file +pub mod base; +pub mod jellyfish; \ No newline at end of file diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index 50fe9d7..26e69b0 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -3,11 +3,7 @@ mod playerlogic; use raylib::prelude::*; -use crate::{ - gamecore::{GameCore, GameState}, - lib::wrappers::audio::player::AudioPlayer, - pallette::{SKY, WATER}, -}; +use crate::{entities::enemy::base::EnemyBase, gamecore::{GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer, pallette::{SKY, WATER}}; use super::screen::Screen; @@ -124,9 +120,11 @@ impl Screen for InGameScreen { fish.update_position(&mut game_core.player, dt, &fish_clone); fish.render(&mut context_2d); } + for jellyfish in game_core.world.jellyfish.iter() { + jellyfish.render(&mut context_2d); + } // Render Player - // playerlogic::render_player(&mut context_2d, game_core); game_core.player.render(&mut context_2d, &mut game_core.resources, dt); } diff --git a/src/world.rs b/src/world.rs index 1f344fe..91441b6 100644 --- a/src/world.rs +++ b/src/world.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use std::io::Read; use failure::Error; -use crate::entities::fish::FishEntity; +use crate::entities::{enemy::jellyfish::JellyFish, fish::FishEntity}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct World { @@ -19,7 +19,9 @@ pub struct World { pub fish: Vec, #[serde(skip)] - pub colliders: Vec + pub colliders: Vec, + + pub jellyfish: Vec } impl World { From c0d147681bb826647e450fdb451ccc5be5d80063 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 17:06:13 -0400 Subject: [PATCH 11/32] handle attacking the jellyfish --- src/logic/ingame/playerlogic.rs | 2 +- src/player.rs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index 86891ba..4d3106e 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -77,7 +77,7 @@ pub fn update_player_movement( let user_request_action = draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON); if user_request_action { - game_core.player.begin_attack(); + game_core.player.begin_attack(&mut game_core.world); } // Move the player in their direction diff --git a/src/player.rs b/src/player.rs index cb49896..479bd03 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,6 +1,6 @@ use raylib::prelude::*; use serde::{Serialize, Deserialize}; -use crate::{gamecore::{GameCore, GameProgress}, items::{AirBag, Flashlight, Flippers, StunGun}, lib::utils::{calculate_linear_slide}, pallette::{TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, resources::GlobalResources, world::World}; +use crate::{entities::enemy::base::EnemyBase, gamecore::{GameCore, GameProgress}, items::{AirBag, Flashlight, Flippers, StunGun}, lib::utils::{calculate_linear_slide}, pallette::{TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, resources::GlobalResources, world::World}; const AOE_RING_MAX_RADIUS: f32 = 60.0; const STUN_ATTACK_TIME: f64 = 0.75; @@ -77,9 +77,18 @@ impl Player { } /// Try to attack with the stun gun - pub fn begin_attack(&mut self) { + pub fn begin_attack(&mut self, world: &mut World) { if self.inventory.stun_gun.is_some() && self.stun_timer == 0.0 { self.attacking_timer = self.inventory.stun_gun.as_ref().unwrap().duration; + + // Stun everything in reach + let stun_reach = self.inventory.stun_gun.as_ref().unwrap().range; + + for jellyfish in world.jellyfish.iter_mut() { + if jellyfish.position.distance_to(self.position).abs() <= stun_reach { + jellyfish.handle_getting_attacked(); + } + } } } From 0aef4804fdc08aac46ad9d91933368e2d3721cc4 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 17:12:40 -0400 Subject: [PATCH 12/32] jelly animation --- src/entities/enemy/base.rs | 4 ++-- src/entities/enemy/jellyfish.rs | 11 +++++++---- src/logic/ingame/mod.rs | 2 +- src/resources.rs | 14 +++++++++++++- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/entities/enemy/base.rs b/src/entities/enemy/base.rs index 04937e6..4fd751f 100644 --- a/src/entities/enemy/base.rs +++ b/src/entities/enemy/base.rs @@ -1,9 +1,9 @@ use raylib::prelude::*; -use crate::player::Player; +use crate::{player::Player, resources::GlobalResources}; pub trait EnemyBase { - fn render(&self, context_2d: &mut RaylibMode2D); + fn render(&self, context_2d: &mut RaylibMode2D, resources: &mut GlobalResources); fn handle_logic(&mut self, player: &mut Player, dt: f64); fn handle_getting_attacked(&mut self); } \ No newline at end of file diff --git a/src/entities/enemy/jellyfish.rs b/src/entities/enemy/jellyfish.rs index 496387f..dbb1af2 100644 --- a/src/entities/enemy/jellyfish.rs +++ b/src/entities/enemy/jellyfish.rs @@ -1,7 +1,7 @@ use super::base::EnemyBase; use raylib::prelude::*; use serde::{Deserialize, Serialize}; -use crate::player::Player; +use crate::{player::Player, resources::GlobalResources}; #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct JellyFish { @@ -17,10 +17,13 @@ impl JellyFish { } impl EnemyBase for JellyFish { - fn render(&self, context_2d: &mut raylib::prelude::RaylibMode2D) { + fn render(&self, context_2d: &mut raylib::prelude::RaylibMode2D, resources: &mut GlobalResources) { + + // Render the jellyfish + resources.jellyfish_animation_regular.draw(context_2d, self.position, 0.0); - // TODO - context_2d.draw_circle_v(self.position, 5.0, Color::RED); + // // TODO + // context_2d.draw_circle_v(self.position, 5.0, Color::RED); } fn handle_logic(&mut self, player: &mut Player, dt: f64) { diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index 26e69b0..ccc51ed 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -121,7 +121,7 @@ impl Screen for InGameScreen { fish.render(&mut context_2d); } for jellyfish in game_core.world.jellyfish.iter() { - jellyfish.render(&mut context_2d); + jellyfish.render(&mut context_2d, &mut game_core.resources); } // Render Player diff --git a/src/resources.rs b/src/resources.rs index b88c5a4..be40c14 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -18,7 +18,10 @@ pub struct GlobalResources { pub player_animation_boost: FrameAnimationWrapper, // Cave - pub cave_mid_layer: Texture2D + pub cave_mid_layer: Texture2D, + + // Enemies + pub jellyfish_animation_regular: FrameAnimationWrapper } impl GlobalResources { @@ -63,6 +66,15 @@ impl GlobalResources { &thread, &Image::load_image("./assets/img/map/cave.png")?, )?, + jellyfish_animation_regular: FrameAnimationWrapper::new( + raylib.load_texture_from_image( + &thread, + &Image::load_image("./assets/img/enemies/jelly.png")?, + )?, + Vector2 { x: 10.0, y: 10.0 }, + 6, + 4, + ), }) } } From 9e0a8df2598b88ae9fdd0e3c4a4b4241b5deddf0 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 17:15:28 -0400 Subject: [PATCH 13/32] jelly movement --- src/entities/enemy/jellyfish.rs | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/entities/enemy/jellyfish.rs b/src/entities/enemy/jellyfish.rs index dbb1af2..72f7061 100644 --- a/src/entities/enemy/jellyfish.rs +++ b/src/entities/enemy/jellyfish.rs @@ -1,29 +1,36 @@ use super::base::EnemyBase; +use crate::{player::Player, resources::GlobalResources}; use raylib::prelude::*; use serde::{Deserialize, Serialize}; -use crate::{player::Player, resources::GlobalResources}; #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct JellyFish { pub position: Vector2, #[serde(skip)] - pub stunned_timer: f64 + pub stunned_timer: f64, } -impl JellyFish { - - -} +impl JellyFish {} impl EnemyBase for JellyFish { - fn render(&self, context_2d: &mut raylib::prelude::RaylibMode2D, resources: &mut GlobalResources) { + fn render( + &self, + context_2d: &mut raylib::prelude::RaylibMode2D, + resources: &mut GlobalResources, + ) { + // Simple sine position + let v_trans = context_2d.get_time().sin(); // Render the jellyfish - resources.jellyfish_animation_regular.draw(context_2d, self.position, 0.0); - - // // TODO - // context_2d.draw_circle_v(self.position, 5.0, Color::RED); + resources.jellyfish_animation_regular.draw( + context_2d, + Vector2 { + x: self.position.x, + y: self.position.y+ (2.0 * v_trans as f32), + }, + 0.0, + ); } fn handle_logic(&mut self, player: &mut Player, dt: f64) { @@ -33,4 +40,4 @@ impl EnemyBase for JellyFish { fn handle_getting_attacked(&mut self) { todo!() } -} \ No newline at end of file +} From 707543cf55a3f24bf32cca894740477c1ac00511 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 17:26:47 -0400 Subject: [PATCH 14/32] jelly --- src/entities/enemy/base.rs | 2 +- src/entities/enemy/jellyfish.rs | 24 +++++++++++++++++++++--- src/lib/wrappers/animation.rs | 2 +- src/logic/ingame/mod.rs | 2 +- src/resources.rs | 12 +++++++++++- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/entities/enemy/base.rs b/src/entities/enemy/base.rs index 4fd751f..83f7af2 100644 --- a/src/entities/enemy/base.rs +++ b/src/entities/enemy/base.rs @@ -3,7 +3,7 @@ use raylib::prelude::*; use crate::{player::Player, resources::GlobalResources}; pub trait EnemyBase { - fn render(&self, context_2d: &mut RaylibMode2D, resources: &mut GlobalResources); + fn render(&mut self, context_2d: &mut RaylibMode2D, resources: &mut GlobalResources); fn handle_logic(&mut self, player: &mut Player, dt: f64); fn handle_getting_attacked(&mut self); } \ No newline at end of file diff --git a/src/entities/enemy/jellyfish.rs b/src/entities/enemy/jellyfish.rs index 72f7061..a657b3b 100644 --- a/src/entities/enemy/jellyfish.rs +++ b/src/entities/enemy/jellyfish.rs @@ -9,13 +9,16 @@ pub struct JellyFish { #[serde(skip)] pub stunned_timer: f64, + + #[serde(skip)] + pub do_stun_player: bool } impl JellyFish {} impl EnemyBase for JellyFish { fn render( - &self, + &mut self, context_2d: &mut raylib::prelude::RaylibMode2D, resources: &mut GlobalResources, ) { @@ -27,14 +30,29 @@ impl EnemyBase for JellyFish { context_2d, Vector2 { x: self.position.x, - y: self.position.y+ (2.0 * v_trans as f32), + y: self.position.y + (2.0 * v_trans as f32), }, 0.0, ); + resources.jellyfish_animation_attack.draw( + context_2d, + Vector2 { + x: self.position.x + , + y: self.position.y + (2.0 * v_trans as f32) + , + }, + 0.0, + ); + self.do_stun_player = resources.jellyfish_animation_attack.get_current_frame_id(context_2d) == 13; } fn handle_logic(&mut self, player: &mut Player, dt: f64) { - todo!() + + // Handle stunning the player + if self.do_stun_player { + + } } fn handle_getting_attacked(&mut self) { diff --git a/src/lib/wrappers/animation.rs b/src/lib/wrappers/animation.rs index 053cf2b..163b70b 100644 --- a/src/lib/wrappers/animation.rs +++ b/src/lib/wrappers/animation.rs @@ -2,7 +2,7 @@ use raylib::{core::color::Color, math::{Rectangle, Vector2}, prelude::{RaylibDra /// A wrapper around an animation spritesheet pub struct FrameAnimationWrapper { - sprite_sheet: Texture2D, + pub sprite_sheet: Texture2D, size: Vector2, frame_count: u32, frames_per_second: u8, diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index ccc51ed..469d761 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -120,7 +120,7 @@ impl Screen for InGameScreen { fish.update_position(&mut game_core.player, dt, &fish_clone); fish.render(&mut context_2d); } - for jellyfish in game_core.world.jellyfish.iter() { + for jellyfish in game_core.world.jellyfish.iter_mut() { jellyfish.render(&mut context_2d, &mut game_core.resources); } diff --git a/src/resources.rs b/src/resources.rs index be40c14..8b8459d 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -21,7 +21,8 @@ pub struct GlobalResources { pub cave_mid_layer: Texture2D, // Enemies - pub jellyfish_animation_regular: FrameAnimationWrapper + pub jellyfish_animation_regular: FrameAnimationWrapper, + pub jellyfish_animation_attack: FrameAnimationWrapper, } impl GlobalResources { @@ -75,6 +76,15 @@ impl GlobalResources { 6, 4, ), + jellyfish_animation_attack: FrameAnimationWrapper::new( + raylib.load_texture_from_image( + &thread, + &Image::load_image("./assets/img/enemies/jellyAttack.png")?, + )?, + Vector2 { x: 20.0, y: 20.0 }, + 15, + 4, + ), }) } } From e7ac70074c88227157bf4eb72932c9dd8d1d500d Mon Sep 17 00:00:00 2001 From: rsninja722 Date: Sat, 24 Apr 2021 17:33:53 -0400 Subject: [PATCH 15/32] no more stuck --- src/logic/ingame/playerlogic.rs | 45 ++++++++++++++++++++------------- src/player.rs | 5 +++- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index 6446ec5..622c20f 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -5,13 +5,13 @@ use crate::{ pallette::{TRANSLUCENT_WHITE_128, TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, }; -const NORMAL_PLAYER_SPEED: i32 = 3; +const NORMAL_PLAYER_SPEED: i32 = 1; const BOOST_PLAYER_SPEED: i32 = NORMAL_PLAYER_SPEED * 2; const CAMERA_FOLLOW_SPEED: f32 = 0.7; const TURN_SPEED: f32 = 0.15; const BOOST_DECREASE_PER_SECOND: f32 = 0.65; const BOOST_REGEN_PER_SECOND: f32 = 0.25; -const BREATH_DECREASE_PER_SECOND: f32 = 0.01; +const BREATH_DECREASE_PER_SECOND: f32 = 0.02; pub fn update_player_movement( draw_handle: &mut RaylibDrawHandle, @@ -127,25 +127,36 @@ pub fn update_player_movement( (game_core.player.breath_percent - BREATH_DECREASE_PER_SECOND * dt as f32).clamp(0.0, 1.0); // Only do this if the mouse is far enough away - let player_real_movement = game_core.player.direction * speed_multiplier; + let mut player_real_movement = game_core.player.direction * speed_multiplier; if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 { - game_core.player.is_moving = true; - game_core.player.position += player_real_movement; - // Check for any collisions - for collider in game_core.world.colliders.iter() { - if game_core.player.collides_with_rec(collider) { - game_core.player.is_moving = false; - break; + if game_core.player.is_moving { + // move in x + game_core.player.position.x += player_real_movement.x; + + // Check for any collisions + for collider in game_core.world.colliders.iter() { + if game_core.player.collides_with_rec(collider) { + + game_core.player.position.x -= player_real_movement.x; + player_real_movement.x = 0.0; + break; + } + } + + // move in y + game_core.player.position.y += player_real_movement.y; + + // Check for any collisions + for collider in game_core.world.colliders.iter() { + if game_core.player.collides_with_rec(collider) { + game_core.player.position.y -= player_real_movement.y; + player_real_movement.y = 0.0; + break; + } } } - - if !game_core.player.is_moving { - game_core.player.position -= player_real_movement; - } - } else { - game_core.player.is_moving = false; - } + } // Move the camera to follow the player let direction_from_cam_to_player = diff --git a/src/player.rs b/src/player.rs index 01c5343..707e677 100644 --- a/src/player.rs +++ b/src/player.rs @@ -7,6 +7,7 @@ pub struct Player { pub position: Vector2, pub direction: Vector2, pub size: Vector2, + pub radius: f32, pub coins: u32, pub boost_percent: f32, pub breath_percent: f32, @@ -21,6 +22,8 @@ impl Player { boost_percent: 1.0, size: Vector2 { x: 11.0, y: 21.0 }, breath_percent: 1.0, + is_moving: true, + radius: 4.5, position: spawn.clone(), ..Default::default() } @@ -54,6 +57,6 @@ impl Player { // || rectangle.check_collision_point_rec(top_right_corner) // || rectangle.check_collision_point_rec(bottom_left_corner); - return rectangle.check_collision_circle_rec(self.position, (self.size.y * 0.5) / 2.0); + return rectangle.check_collision_circle_rec(self.position, self.radius); } } From 09b2752d780963b320acbd9769737c268e3ac545 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 18:10:27 -0400 Subject: [PATCH 16/32] stun check --- src/entities/enemy/jellyfish.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/entities/enemy/jellyfish.rs b/src/entities/enemy/jellyfish.rs index a657b3b..cf6ca10 100644 --- a/src/entities/enemy/jellyfish.rs +++ b/src/entities/enemy/jellyfish.rs @@ -11,7 +11,7 @@ pub struct JellyFish { pub stunned_timer: f64, #[serde(skip)] - pub do_stun_player: bool + pub do_stun_player: bool, } impl JellyFish {} @@ -37,22 +37,20 @@ impl EnemyBase for JellyFish { resources.jellyfish_animation_attack.draw( context_2d, Vector2 { - x: self.position.x - , - y: self.position.y + (2.0 * v_trans as f32) - , + x: self.position.x, + y: self.position.y + (2.0 * v_trans as f32), }, 0.0, ); - self.do_stun_player = resources.jellyfish_animation_attack.get_current_frame_id(context_2d) == 13; + self.do_stun_player = resources + .jellyfish_animation_attack + .get_current_frame_id(context_2d) + == 13; } fn handle_logic(&mut self, player: &mut Player, dt: f64) { - // Handle stunning the player - if self.do_stun_player { - - } + if self.do_stun_player {} } fn handle_getting_attacked(&mut self) { From 9493baedf54a6b0ad609fafb40e1613a5cc56201 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 18:28:27 -0400 Subject: [PATCH 17/32] stun the jellyfish --- src/entities/enemy/base.rs | 11 +++-- src/entities/enemy/jellyfish.rs | 71 +++++++++++++++++++++++---------- src/items.rs | 4 +- src/logic/ingame/mod.rs | 21 +++++----- src/pallette.rs | 7 ++++ src/player.rs | 32 ++++++++++++--- 6 files changed, 104 insertions(+), 42 deletions(-) diff --git a/src/entities/enemy/base.rs b/src/entities/enemy/base.rs index 83f7af2..d5c3f68 100644 --- a/src/entities/enemy/base.rs +++ b/src/entities/enemy/base.rs @@ -3,7 +3,12 @@ use raylib::prelude::*; use crate::{player::Player, resources::GlobalResources}; pub trait EnemyBase { - fn render(&mut self, context_2d: &mut RaylibMode2D, resources: &mut GlobalResources); + fn render( + &mut self, + context_2d: &mut RaylibMode2D, + resources: &mut GlobalResources, + dt: f64, + ); fn handle_logic(&mut self, player: &mut Player, dt: f64); - fn handle_getting_attacked(&mut self); -} \ No newline at end of file + fn handle_getting_attacked(&mut self, stun_duration: f64); +} diff --git a/src/entities/enemy/jellyfish.rs b/src/entities/enemy/jellyfish.rs index cf6ca10..74a79e5 100644 --- a/src/entities/enemy/jellyfish.rs +++ b/src/entities/enemy/jellyfish.rs @@ -1,5 +1,8 @@ use super::base::EnemyBase; -use crate::{player::Player, resources::GlobalResources}; +use crate::{ + lib::utils::calculate_linear_slide, pallette::TRANSLUCENT_RED_64, player::Player, + resources::GlobalResources, +}; use raylib::prelude::*; use serde::{Deserialize, Serialize}; @@ -9,6 +12,8 @@ pub struct JellyFish { #[serde(skip)] pub stunned_timer: f64, + #[serde(skip)] + pub max_stunned_time: f64, #[serde(skip)] pub do_stun_player: bool, @@ -21,31 +26,51 @@ impl EnemyBase for JellyFish { &mut self, context_2d: &mut raylib::prelude::RaylibMode2D, resources: &mut GlobalResources, + dt: f64 ) { + let is_jelly_stunned = self.stunned_timer != 0.0; + // Simple sine position - let v_trans = context_2d.get_time().sin(); + let v_trans = if is_jelly_stunned { + 0.0 + } else { + context_2d.get_time().sin() + }; + let trans_pose = Vector2 { + x: self.position.x, + y: self.position.y + (2.0 * v_trans as f32), + }; + + // Render the stun ring + if self.max_stunned_time > 0.0 && self.stunned_timer > 0.0 { + let stun_ring_radius = + calculate_linear_slide(self.stunned_timer / self.max_stunned_time); + context_2d.draw_circle_v( + trans_pose, + stun_ring_radius as f32 * 20.0, + TRANSLUCENT_RED_64, + ); + self.stunned_timer -= dt; + } // Render the jellyfish - resources.jellyfish_animation_regular.draw( - context_2d, - Vector2 { - x: self.position.x, - y: self.position.y + (2.0 * v_trans as f32), - }, - 0.0, - ); - resources.jellyfish_animation_attack.draw( - context_2d, - Vector2 { - x: self.position.x, - y: self.position.y + (2.0 * v_trans as f32), - }, - 0.0, - ); - self.do_stun_player = resources + resources + .jellyfish_animation_regular + .draw(context_2d, trans_pose, 0.0); + + // Only do stun loop if not stunned + if !is_jelly_stunned { + resources + .jellyfish_animation_attack + .draw(context_2d, trans_pose, 0.0); + } + + // Check if the jelly is in stun mode + self.do_stun_player = (resources .jellyfish_animation_attack .get_current_frame_id(context_2d) - == 13; + == 13) + && !is_jelly_stunned; } fn handle_logic(&mut self, player: &mut Player, dt: f64) { @@ -53,7 +78,9 @@ impl EnemyBase for JellyFish { if self.do_stun_player {} } - fn handle_getting_attacked(&mut self) { - todo!() + fn handle_getting_attacked(&mut self, stun_duration: f64) { + println!("Attack"); + self.stunned_timer = stun_duration; + self.max_stunned_time = stun_duration; } } diff --git a/src/items.rs b/src/items.rs index 60d99a3..90ae94d 100644 --- a/src/items.rs +++ b/src/items.rs @@ -10,13 +10,13 @@ impl StunGun { pub fn lvl1() -> Self { Self { range: 30.0, - duration: 0.5, + duration: 0.75, } } pub fn lvl2() -> Self { Self { range: 60.0, - duration: 0.75, + duration: 1.25, } } } diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index 469d761..f3b6cb8 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -3,7 +3,12 @@ mod playerlogic; use raylib::prelude::*; -use crate::{entities::enemy::base::EnemyBase, gamecore::{GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer, pallette::{SKY, WATER}}; +use crate::{ + entities::enemy::base::EnemyBase, + gamecore::{GameCore, GameState}, + lib::wrappers::audio::player::AudioPlayer, + pallette::{SKY, WATER}, +}; use super::screen::Screen; @@ -64,11 +69,7 @@ impl InGameScreen { ) { // Render every collider for collider in game_core.world.colliders.iter() { - context_2d.draw_rectangle_lines_ex( - collider, - 1, - Color::RED, - ); + context_2d.draw_rectangle_lines_ex(collider, 1, Color::RED); } } } @@ -110,7 +111,7 @@ impl Screen for InGameScreen { // Render the world self.render_world(&mut context_2d, game_core); - if game_core.show_simple_debug_info{ + if game_core.show_simple_debug_info { self.render_colliders(&mut context_2d, game_core); } @@ -121,11 +122,13 @@ impl Screen for InGameScreen { fish.render(&mut context_2d); } for jellyfish in game_core.world.jellyfish.iter_mut() { - jellyfish.render(&mut context_2d, &mut game_core.resources); + jellyfish.render(&mut context_2d, &mut game_core.resources, dt); } // Render Player - game_core.player.render(&mut context_2d, &mut game_core.resources, dt); + game_core + .player + .render(&mut context_2d, &mut game_core.resources, dt); } // Render the hud diff --git a/src/pallette.rs b/src/pallette.rs index 5d675ca..b6cb182 100644 --- a/src/pallette.rs +++ b/src/pallette.rs @@ -33,4 +33,11 @@ pub const WATER: Color = Color { g: 66, b: 143, a: 255 +}; + +pub const TRANSLUCENT_RED_64: Color = Color { + r: 230, + g: 41, + b: 55, + a: 64, }; \ No newline at end of file diff --git a/src/player.rs b/src/player.rs index d2335bf..cafded3 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,6 +1,14 @@ +use crate::{ + entities::enemy::base::EnemyBase, + gamecore::{GameCore, GameProgress}, + items::{AirBag, Flashlight, Flippers, StunGun}, + lib::utils::calculate_linear_slide, + pallette::{TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, + resources::GlobalResources, + world::World, +}; use raylib::prelude::*; -use serde::{Serialize, Deserialize}; -use crate::{entities::enemy::base::EnemyBase, gamecore::{GameCore, GameProgress}, items::{AirBag, Flashlight, Flippers, StunGun}, lib::utils::{calculate_linear_slide}, pallette::{TRANSLUCENT_WHITE_64, TRANSLUCENT_WHITE_96}, resources::GlobalResources, world::World}; +use serde::{Deserialize, Serialize}; const AOE_RING_MAX_RADIUS: f32 = 60.0; const STUN_ATTACK_TIME: f64 = 0.75; @@ -10,7 +18,16 @@ pub struct PlayerInventory { pub stun_gun: Option, pub air_bag: Option, pub flashlight: Option, - pub flippers: Option + pub flippers: Option, +} + +impl PlayerInventory { + pub fn new() -> Self { + Self { + stun_gun: Some(StunGun::lvl1()), //TMP + ..Default::default() + } + } } #[derive(Debug, Default)] @@ -39,6 +56,7 @@ impl Player { is_moving: true, radius: 4.5, position: spawn.clone(), + inventory: PlayerInventory::new(), ..Default::default() } } @@ -84,12 +102,12 @@ impl Player { if self.inventory.stun_gun.is_some() && self.stun_timer == 0.0 { self.attacking_timer = self.inventory.stun_gun.as_ref().unwrap().duration; - // Stun everything in reach + // Stun everything in reach let stun_reach = self.inventory.stun_gun.as_ref().unwrap().range; for jellyfish in world.jellyfish.iter_mut() { if jellyfish.position.distance_to(self.position).abs() <= stun_reach { - jellyfish.handle_getting_attacked(); + jellyfish.handle_getting_attacked(self.attacking_timer); } } } @@ -151,7 +169,9 @@ impl Player { // Calculate AOE ring if self.is_stun_gun_active() { - let aoe_ring = calculate_linear_slide( self.attacking_timer / self.inventory.stun_gun.as_ref().unwrap().duration) as f32; + let aoe_ring = calculate_linear_slide( + self.attacking_timer / self.inventory.stun_gun.as_ref().unwrap().duration, + ) as f32; self.attacking_timer = (self.attacking_timer - dt).max(0.0); // Render attack AOE From 99e7e02d0c045ae75be8810221f6d974887ca787 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 18:32:08 -0400 Subject: [PATCH 18/32] Better AOE animation --- src/player.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/player.rs b/src/player.rs index cafded3..341d70e 100644 --- a/src/player.rs +++ b/src/player.rs @@ -169,18 +169,27 @@ impl Player { // Calculate AOE ring if self.is_stun_gun_active() { - let aoe_ring = calculate_linear_slide( - self.attacking_timer / self.inventory.stun_gun.as_ref().unwrap().duration, - ) as f32; + let animation_progression = + self.attacking_timer / self.inventory.stun_gun.as_ref().unwrap().duration; + let aoe_ring = calculate_linear_slide(animation_progression) as f32; self.attacking_timer = (self.attacking_timer - dt).max(0.0); // Render attack AOE - context_2d.draw_circle_lines( - self.position.x as i32, - self.position.y as i32, - self.inventory.stun_gun.as_ref().unwrap().range * aoe_ring, - TRANSLUCENT_WHITE_64, - ); + if animation_progression <= 0.5 { + context_2d.draw_circle_lines( + self.position.x as i32, + self.position.y as i32, + self.inventory.stun_gun.as_ref().unwrap().range * aoe_ring, + TRANSLUCENT_WHITE_64, + ); + } else { + context_2d.draw_circle_lines( + self.position.x as i32, + self.position.y as i32, + self.inventory.stun_gun.as_ref().unwrap().range, + TRANSLUCENT_WHITE_64.fade(aoe_ring), + ); + } } // Render the player based on what is happening From 91482353836a308cc19d4c9f2a6a25f1f68a70de Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 18:45:13 -0400 Subject: [PATCH 19/32] jelly can stun player --- src/entities/enemy/jellyfish.rs | 20 +++++++++++++------- src/logic/ingame/mod.rs | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/entities/enemy/jellyfish.rs b/src/entities/enemy/jellyfish.rs index 74a79e5..4ddad72 100644 --- a/src/entities/enemy/jellyfish.rs +++ b/src/entities/enemy/jellyfish.rs @@ -6,6 +6,9 @@ use crate::{ use raylib::prelude::*; use serde::{Deserialize, Serialize}; +const JELLYFISH_STUN_DURATION: f64 = 0.75; +const JELLYFISH_STUN_REACH: f32 = 20.0; + #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct JellyFish { pub position: Vector2, @@ -26,9 +29,9 @@ impl EnemyBase for JellyFish { &mut self, context_2d: &mut raylib::prelude::RaylibMode2D, resources: &mut GlobalResources, - dt: f64 + dt: f64, ) { - let is_jelly_stunned = self.stunned_timer != 0.0; + let is_jelly_stunned = self.stunned_timer > 0.0; // Simple sine position let v_trans = if is_jelly_stunned { @@ -43,12 +46,12 @@ impl EnemyBase for JellyFish { // Render the stun ring if self.max_stunned_time > 0.0 && self.stunned_timer > 0.0 { - let stun_ring_radius = + let stun_ring_alpha = calculate_linear_slide(self.stunned_timer / self.max_stunned_time); context_2d.draw_circle_v( trans_pose, - stun_ring_radius as f32 * 20.0, - TRANSLUCENT_RED_64, + JELLYFISH_STUN_REACH, + TRANSLUCENT_RED_64.fade(0.55 * stun_ring_alpha as f32), ); self.stunned_timer -= dt; } @@ -75,11 +78,14 @@ impl EnemyBase for JellyFish { fn handle_logic(&mut self, player: &mut Player, dt: f64) { // Handle stunning the player - if self.do_stun_player {} + if self.do_stun_player { + if self.position.distance_to(player.position).abs() <= JELLYFISH_STUN_REACH { + player.set_stun_seconds(JELLYFISH_STUN_DURATION); + } + } } fn handle_getting_attacked(&mut self, stun_duration: f64) { - println!("Attack"); self.stunned_timer = stun_duration; self.max_stunned_time = stun_duration; } diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index f3b6cb8..a9a75c0 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -122,6 +122,7 @@ impl Screen for InGameScreen { fish.render(&mut context_2d); } for jellyfish in game_core.world.jellyfish.iter_mut() { + jellyfish.handle_logic(&mut game_core.player, dt); jellyfish.render(&mut context_2d, &mut game_core.resources, dt); } From 0659780b3d414ab28f8894707b8a74eea6044904 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 18:46:13 -0400 Subject: [PATCH 20/32] load stunned animation --- assets/img/character/stunned.json | 50 ++++++++++++++++++++++++++++++ assets/img/character/stunned.png | Bin 0 -> 841 bytes 2 files changed, 50 insertions(+) create mode 100644 assets/img/character/stunned.json create mode 100644 assets/img/character/stunned.png diff --git a/assets/img/character/stunned.json b/assets/img/character/stunned.json new file mode 100644 index 0000000..5d29dfc --- /dev/null +++ b/assets/img/character/stunned.json @@ -0,0 +1,50 @@ +{ "frames": { + "walk1 0.png": { + "frame": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "sourceSize": { "w": 12, "h": 22 }, + "duration": 200 + }, + "walk1 1.png": { + "frame": { "x": 12, "y": 0, "w": 12, "h": 22 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "sourceSize": { "w": 12, "h": 22 }, + "duration": 200 + }, + "walk1 2.png": { + "frame": { "x": 24, "y": 0, "w": 12, "h": 22 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "sourceSize": { "w": 12, "h": 22 }, + "duration": 200 + }, + "walk1 3.png": { + "frame": { "x": 36, "y": 0, "w": 12, "h": 22 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "sourceSize": { "w": 12, "h": 22 }, + "duration": 200 + } + }, + "meta": { + "app": "http://www.aseprite.org/", + "version": "1.2.27-x64", + "image": "stunned.png", + "format": "RGBA8888", + "size": { "w": 48, "h": 22 }, + "scale": "1", + "frameTags": [ + ], + "layers": [ + { "name": "Layer", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/assets/img/character/stunned.png b/assets/img/character/stunned.png new file mode 100644 index 0000000000000000000000000000000000000000..986e8f0a3b0273c2b4307dfeb19c162b6780fb6e GIT binary patch literal 841 zcmV-P1GfB$P)Px&14%?dR9J=8mrrQZP#nj--BF{tiV&2P3`Z92^9~45~ z=llKr-kldhz!D8!|LItN_4bIX4FY9q%Tl=r|n<^8A?Sp2$DWBWVtmzX<@N z1FO@c1FO@2df!Ky$0d#{RMtZ_hZ>jY$j1vXv@{mWxjDKk?ZVK~kz=BX#PKpYRKS}} z%CwO(Z6=AKrSZvp;O|Jg#8Jfz%?>MumWH9FL;kXbM%_~Z@62Buy{G8)JW{630LNRs zi4sJGIcX_LH8Q*kcxN5}P|$$B4gi2%tsQIh??SKVAv`_>&HL@KE9Es3EL0errqRC} zU%f;Q<{4--UFe-7u&Xs<_MH7qO-_EkG-VenglG4R88#TQ8oO8_JS&sKE>;LLfk%&@ z_@b5a8adsi`(sXP2eK?RPom-3c0Bbz%8Mw(7sQ{%`OGBn0YHIe_f{+NyvzFgJJO{I zd8$z Date: Sat, 24 Apr 2021 18:54:23 -0400 Subject: [PATCH 21/32] finished stun system --- src/logic/ingame/playerlogic.rs | 8 ++++++-- src/player.rs | 14 ++++++++++++-- src/resources.rs | 10 ++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index d9ca234..cdb261d 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -65,7 +65,9 @@ pub fn update_player_movement( } // set angle - game_core.player.direction = Vector2::new(f32::cos(player_angle), f32::sin(player_angle)); + if !game_core.player.is_stunned() { + game_core.player.direction = Vector2::new(f32::cos(player_angle), f32::sin(player_angle)); + } // In the case the player is in "null", just jump the camera to them if game_core.player.position == Vector2::zero() { @@ -145,7 +147,9 @@ pub fn update_player_movement( // Only do this if the mouse is far enough away let player_stunned = game_core.player.stun_timer > 0.0; let mut player_real_movement = game_core.player.direction * speed_multiplier; - if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 { + if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 + && !game_core.player.is_stunned() + { if game_core.player.is_moving { // move in x game_core.player.position.x += player_real_movement.x; diff --git a/src/player.rs b/src/player.rs index 341d70e..efcb5ea 100644 --- a/src/player.rs +++ b/src/player.rs @@ -99,7 +99,7 @@ impl Player { /// Try to attack with the stun gun pub fn begin_attack(&mut self, world: &mut World) { - if self.inventory.stun_gun.is_some() && self.stun_timer == 0.0 { + if self.inventory.stun_gun.is_some() && !self.is_stunned() { self.attacking_timer = self.inventory.stun_gun.as_ref().unwrap().duration; // Stun everything in reach @@ -117,6 +117,10 @@ impl Player { return self.attacking_timer != 0.0 && self.inventory.stun_gun.is_some(); } + pub fn is_stunned(&self) -> bool { + return self.stun_timer > 0.0; + } + /// Calculate how far the player is pub fn calculate_depth_percent(&self, world: &World) -> f32 { let dist_from_player_to_end = self.position.distance_to(world.end_position); @@ -193,7 +197,13 @@ impl Player { } // Render the player based on what is happening - if self.is_boost_charging { + if self.is_stunned() { + resources.player_animation_stunned.draw( + context_2d, + self.position, + player_rotation.to_degrees() - 90.0, + ); + } else if self.is_boost_charging { resources.player_animation_boost_charge.draw( context_2d, self.position, diff --git a/src/resources.rs b/src/resources.rs index 8b8459d..51d115c 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -16,6 +16,7 @@ pub struct GlobalResources { pub player_animation_regular: FrameAnimationWrapper, pub player_animation_boost_charge: FrameAnimationWrapper, pub player_animation_boost: FrameAnimationWrapper, + pub player_animation_stunned: FrameAnimationWrapper, // Cave pub cave_mid_layer: Texture2D, @@ -63,6 +64,15 @@ impl GlobalResources { 21, 30, ), + player_animation_stunned: FrameAnimationWrapper::new( + raylib.load_texture_from_image( + &thread, + &Image::load_image("./assets/img/character/stunned.png")?, + )?, + Vector2 { x: 12.0, y: 22.0 }, + 4, + 100 / 8, + ), cave_mid_layer: raylib.load_texture_from_image( &thread, &Image::load_image("./assets/img/map/cave.png")?, From f9957f57ea96a02692dc3125a6dd2755def27d72 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 18:58:20 -0400 Subject: [PATCH 22/32] reverse stun animation --- src/player.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/player.rs b/src/player.rs index efcb5ea..0d30883 100644 --- a/src/player.rs +++ b/src/player.rs @@ -179,12 +179,12 @@ impl Player { self.attacking_timer = (self.attacking_timer - dt).max(0.0); // Render attack AOE - if animation_progression <= 0.5 { + if animation_progression >= 0.5 { context_2d.draw_circle_lines( self.position.x as i32, self.position.y as i32, self.inventory.stun_gun.as_ref().unwrap().range * aoe_ring, - TRANSLUCENT_WHITE_64, + TRANSLUCENT_WHITE_64.fade(aoe_ring), ); } else { context_2d.draw_circle_lines( From 81126975a122082b8b1cb88885d678808a87270c Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 19:02:03 -0400 Subject: [PATCH 23/32] fix fish render layer --- src/logic/ingame/mod.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index a9a75c0..7c04afc 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -32,6 +32,7 @@ impl InGameScreen { &mut self, context_2d: &mut RaylibMode2D, game_core: &mut GameCore, + dt: f64 ) { // Build source bounds let source_bounds = Rectangle { @@ -50,6 +51,13 @@ impl InGameScreen { // Clear the background context_2d.draw_rectangle_rec(world_bounds, WATER); + // Render fish + let fish_clone = game_core.world.fish.clone(); + for fish in game_core.world.fish.iter_mut() { + fish.update_position(&mut game_core.player, dt, &fish_clone); + fish.render(context_2d); + } + // Render the world texture context_2d.draw_texture_rec( &game_core.resources.cave_mid_layer, @@ -107,20 +115,15 @@ impl Screen for InGameScreen { // Open a 2D context { - let mut context_2d = draw_handle.begin_mode2D(game_core.master_camera); + let mut context_2d = draw_handle.begin_mode2D(game_core.master_camera); // Render the world - self.render_world(&mut context_2d, game_core); + self.render_world(&mut context_2d, game_core, dt); if game_core.show_simple_debug_info { self.render_colliders(&mut context_2d, game_core); } // Render entities - let fish_clone = game_core.world.fish.clone(); - for fish in game_core.world.fish.iter_mut() { - fish.update_position(&mut game_core.player, dt, &fish_clone); - fish.render(&mut context_2d); - } for jellyfish in game_core.world.jellyfish.iter_mut() { jellyfish.handle_logic(&mut game_core.player, dt); jellyfish.render(&mut context_2d, &mut game_core.resources, dt); From 1585521fe31b7e9e69d4a1c3d81ee721cb8604d0 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 19:09:28 -0400 Subject: [PATCH 24/32] background gradient --- src/logic/ingame/mod.rs | 20 +++++++++++--------- src/pallette.rs | 7 +++++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index 7c04afc..5d11c49 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -3,12 +3,7 @@ mod playerlogic; use raylib::prelude::*; -use crate::{ - entities::enemy::base::EnemyBase, - gamecore::{GameCore, GameState}, - lib::wrappers::audio::player::AudioPlayer, - pallette::{SKY, WATER}, -}; +use crate::{entities::enemy::base::EnemyBase, gamecore::{GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer, pallette::{SKY, WATER, WATER_DARK}}; use super::screen::Screen; @@ -32,7 +27,7 @@ impl InGameScreen { &mut self, context_2d: &mut RaylibMode2D, game_core: &mut GameCore, - dt: f64 + dt: f64, ) { // Build source bounds let source_bounds = Rectangle { @@ -49,7 +44,14 @@ impl InGameScreen { }; // Clear the background - context_2d.draw_rectangle_rec(world_bounds, WATER); + context_2d.draw_rectangle_gradient_v( + world_bounds.x as i32, + world_bounds.y as i32, + world_bounds.width as i32, + world_bounds.height as i32, + WATER, + WATER_DARK, + ); // Render fish let fish_clone = game_core.world.fish.clone(); @@ -115,7 +117,7 @@ impl Screen for InGameScreen { // Open a 2D context { - let mut context_2d = draw_handle.begin_mode2D(game_core.master_camera); + let mut context_2d = draw_handle.begin_mode2D(game_core.master_camera); // Render the world self.render_world(&mut context_2d, game_core, dt); diff --git a/src/pallette.rs b/src/pallette.rs index b6cb182..65f2243 100644 --- a/src/pallette.rs +++ b/src/pallette.rs @@ -35,6 +35,13 @@ pub const WATER: Color = Color { a: 255 }; +pub const WATER_DARK: Color = Color { + r: 8, + g: 24, + b: 54, + a: 255 +}; + pub const TRANSLUCENT_RED_64: Color = Color { r: 230, g: 41, From cf342aa9329351616b5eb25cddfde4f41f74c3b7 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 19:39:09 -0400 Subject: [PATCH 25/32] octopus movement --- assets/worlds/mainworld.json | 12 +++++++++ src/entities/enemy/mod.rs | 3 ++- src/entities/enemy/octopus.rs | 50 +++++++++++++++++++++++++++++++++++ src/logic/ingame/mod.rs | 4 +++ src/world.rs | 6 +++-- 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/entities/enemy/octopus.rs diff --git a/assets/worlds/mainworld.json b/assets/worlds/mainworld.json index c5c7288..4be5d70 100644 --- a/assets/worlds/mainworld.json +++ b/assets/worlds/mainworld.json @@ -17,5 +17,17 @@ "y": 100 } } + ], + "octopus": [ + { + "position_a" : { + "x": 300, + "y": 150 + }, + "position_b" : { + "x": 500, + "y": 100 + } + } ] } \ No newline at end of file diff --git a/src/entities/enemy/mod.rs b/src/entities/enemy/mod.rs index ea64b37..1189f2b 100644 --- a/src/entities/enemy/mod.rs +++ b/src/entities/enemy/mod.rs @@ -1,2 +1,3 @@ pub mod base; -pub mod jellyfish; \ No newline at end of file +pub mod jellyfish; +pub mod octopus; \ No newline at end of file diff --git a/src/entities/enemy/octopus.rs b/src/entities/enemy/octopus.rs new file mode 100644 index 0000000..50de4ac --- /dev/null +++ b/src/entities/enemy/octopus.rs @@ -0,0 +1,50 @@ +use super::base::EnemyBase; +use raylib::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +pub struct Octopus { + pub position_a: Vector2, + pub position_b: Vector2, + + #[serde(skip)] + pub current_position: Vector2, + + #[serde(skip)] + pub stunned_timer: f64, + #[serde(skip)] + pub max_stunned_time: f64, +} + +impl Octopus {} + +impl EnemyBase for Octopus { + fn render( + &mut self, + context_2d: &mut raylib::prelude::RaylibMode2D, + resources: &mut crate::resources::GlobalResources, + dt: f64, + ) { + let is_octopus_stunned = self.stunned_timer > 0.0; + + // Simple sine position + let h_trans = if is_octopus_stunned { + 0.0 + } else { + (context_2d.get_time() / 8.0).sin().abs() as f32 + }; + + // Modify the current pose + let dist_a_to_b = self.position_b - self.position_a; + self.current_position =(dist_a_to_b * h_trans) + self.position_a; + + // TODO: TMP + context_2d.draw_circle_v(self.current_position, 10.0, Color::RED); + } + + fn handle_logic(&mut self, player: &mut crate::player::Player, dt: f64) {} + + fn handle_getting_attacked(&mut self, stun_duration: f64) { + todo!() + } +} diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index 5d11c49..4b00f0e 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -130,6 +130,10 @@ impl Screen for InGameScreen { jellyfish.handle_logic(&mut game_core.player, dt); jellyfish.render(&mut context_2d, &mut game_core.resources, dt); } + for octopus in game_core.world.octopus.iter_mut() { + octopus.handle_logic(&mut game_core.player, dt); + octopus.render(&mut context_2d, &mut game_core.resources, dt); + } // Render Player game_core diff --git a/src/world.rs b/src/world.rs index 91441b6..b4237c4 100644 --- a/src/world.rs +++ b/src/world.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use std::io::Read; use failure::Error; -use crate::entities::{enemy::jellyfish::JellyFish, fish::FishEntity}; +use crate::entities::{enemy::{jellyfish::JellyFish, octopus::Octopus}, fish::FishEntity}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct World { @@ -21,7 +21,9 @@ pub struct World { #[serde(skip)] pub colliders: Vec, - pub jellyfish: Vec + pub jellyfish: Vec, + pub octopus: Vec, + } impl World { From c5a2877efd89bbc806fd88ab564d8339a30323f6 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 20:07:19 -0400 Subject: [PATCH 26/32] suck air bubble animation --- src/entities/enemy/base.rs | 3 +- src/entities/enemy/jellyfish.rs | 3 +- src/entities/enemy/octopus.rs | 88 +++++++++++++++++++++++++++++---- src/logic/ingame/mod.rs | 21 ++++++-- src/logic/ingame/playerlogic.rs | 2 +- src/player.rs | 9 +++- 6 files changed, 109 insertions(+), 17 deletions(-) diff --git a/src/entities/enemy/base.rs b/src/entities/enemy/base.rs index d5c3f68..ddddc49 100644 --- a/src/entities/enemy/base.rs +++ b/src/entities/enemy/base.rs @@ -6,9 +6,10 @@ pub trait EnemyBase { fn render( &mut self, context_2d: &mut RaylibMode2D, + player: &mut Player, resources: &mut GlobalResources, dt: f64, ); fn handle_logic(&mut self, player: &mut Player, dt: f64); - fn handle_getting_attacked(&mut self, stun_duration: f64); + fn handle_getting_attacked(&mut self, stun_duration: f64, current_time: f64); } diff --git a/src/entities/enemy/jellyfish.rs b/src/entities/enemy/jellyfish.rs index 4ddad72..7537fdc 100644 --- a/src/entities/enemy/jellyfish.rs +++ b/src/entities/enemy/jellyfish.rs @@ -28,6 +28,7 @@ impl EnemyBase for JellyFish { fn render( &mut self, context_2d: &mut raylib::prelude::RaylibMode2D, + player: &mut Player, resources: &mut GlobalResources, dt: f64, ) { @@ -85,7 +86,7 @@ impl EnemyBase for JellyFish { } } - fn handle_getting_attacked(&mut self, stun_duration: f64) { + fn handle_getting_attacked(&mut self, stun_duration: f64, current_time: f64) { self.stunned_timer = stun_duration; self.max_stunned_time = stun_duration; } diff --git a/src/entities/enemy/octopus.rs b/src/entities/enemy/octopus.rs index 50de4ac..f553e97 100644 --- a/src/entities/enemy/octopus.rs +++ b/src/entities/enemy/octopus.rs @@ -1,7 +1,25 @@ +use crate::{ + lib::utils::calculate_linear_slide, + pallette::{TRANSLUCENT_RED_64, TRANSLUCENT_WHITE_64}, + player::Player, +}; + use super::base::EnemyBase; +use rand::{prelude::ThreadRng, Rng}; use raylib::prelude::*; use serde::{Deserialize, Serialize}; +const OCTOPUS_SUCK_AIR_DELAY: f64 = 3.5; +const OCTOPUS_SUCK_AIR_RANGE: f32 = 70.0; +const OCTOPUS_SUCK_AIR_DURATION: f64 = 1.0; +// const RNG: ThreadRng = rand::thread_rng(); + +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +struct OctopusAirBubble { + position: Vector2, + speed: f32, +} + #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct Octopus { pub position_a: Vector2, @@ -14,6 +32,11 @@ pub struct Octopus { pub stunned_timer: f64, #[serde(skip)] pub max_stunned_time: f64, + + #[serde(skip)] + pub suck_air_time_remaining: f64, + #[serde(skip)] + suck_air_bubbles: Vec, } impl Octopus {} @@ -22,29 +45,76 @@ impl EnemyBase for Octopus { fn render( &mut self, context_2d: &mut raylib::prelude::RaylibMode2D, + player: &mut Player, resources: &mut crate::resources::GlobalResources, dt: f64, ) { let is_octopus_stunned = self.stunned_timer > 0.0; // Simple sine position - let h_trans = if is_octopus_stunned { - 0.0 - } else { - (context_2d.get_time() / 8.0).sin().abs() as f32 - }; + let h_trans = (context_2d.get_time() / 8.0).sin().abs() as f32; // Modify the current pose let dist_a_to_b = self.position_b - self.position_a; - self.current_position =(dist_a_to_b * h_trans) + self.position_a; + self.current_position = (dist_a_to_b * h_trans) + self.position_a; + + // Render the stun ring + if self.max_stunned_time > 0.0 && self.stunned_timer > 0.0 { + let stun_ring_alpha = + calculate_linear_slide(self.stunned_timer / self.max_stunned_time); + context_2d.draw_circle_v( + self.current_position, + 20.0, + TRANSLUCENT_RED_64.fade(0.55 * stun_ring_alpha as f32), + ); + self.stunned_timer -= dt; + } + + // Every once in a while, start sucking air + if (context_2d.get_time() % OCTOPUS_SUCK_AIR_DELAY) < 0.1 { + self.suck_air_time_remaining = OCTOPUS_SUCK_AIR_DURATION; + + // Spawn a few air bubbles if the player is in range + if player.position.distance_to(self.current_position).abs() <= OCTOPUS_SUCK_AIR_RANGE { + for _ in 0..3 { + self.suck_air_bubbles.push(OctopusAirBubble { + position: player.position, + speed: rand::thread_rng().gen_range(0.8..1.3), + }); + } + } + } + + // Handle sucking air bubble animation + if self.suck_air_time_remaining > 0.0 { + // Render and update all bubbles + for bubble in self.suck_air_bubbles.iter_mut() { + // Get the direction from the bubble to the octopus + let direction = (self.current_position - bubble.position).normalized(); + + // Render the bubble + context_2d.draw_circle_v(bubble.position, 2.0, TRANSLUCENT_WHITE_64); + + // Move the bubble + bubble.position += direction * bubble.speed; + } + + // Reduce time + self.suck_air_time_remaining -= dt; + } else { + self.suck_air_bubbles.clear(); + } // TODO: TMP context_2d.draw_circle_v(self.current_position, 10.0, Color::RED); } - fn handle_logic(&mut self, player: &mut crate::player::Player, dt: f64) {} + fn handle_logic(&mut self, player: &mut crate::player::Player, dt: f64) { + if self.suck_air_time_remaining > 0.0 {} + } - fn handle_getting_attacked(&mut self, stun_duration: f64) { - todo!() + fn handle_getting_attacked(&mut self, stun_duration: f64, current_time: f64) { + self.stunned_timer = stun_duration; + self.max_stunned_time = stun_duration; } } diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index 4b00f0e..2cc67f9 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -3,7 +3,12 @@ mod playerlogic; use raylib::prelude::*; -use crate::{entities::enemy::base::EnemyBase, gamecore::{GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer, pallette::{SKY, WATER, WATER_DARK}}; +use crate::{ + entities::enemy::base::EnemyBase, + gamecore::{GameCore, GameState}, + lib::wrappers::audio::player::AudioPlayer, + pallette::{SKY, WATER, WATER_DARK}, +}; use super::screen::Screen; @@ -128,11 +133,21 @@ impl Screen for InGameScreen { // Render entities for jellyfish in game_core.world.jellyfish.iter_mut() { jellyfish.handle_logic(&mut game_core.player, dt); - jellyfish.render(&mut context_2d, &mut game_core.resources, dt); + jellyfish.render( + &mut context_2d, + &mut game_core.player, + &mut game_core.resources, + dt, + ); } for octopus in game_core.world.octopus.iter_mut() { octopus.handle_logic(&mut game_core.player, dt); - octopus.render(&mut context_2d, &mut game_core.resources, dt); + octopus.render( + &mut context_2d, + &mut game_core.player, + &mut game_core.resources, + dt, + ); } // Render Player diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index cdb261d..a506daa 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -79,7 +79,7 @@ pub fn update_player_movement( let user_request_action = draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON); if user_request_action { - game_core.player.begin_attack(&mut game_core.world); + game_core.player.begin_attack(&mut game_core.world, draw_handle.get_time()); } // Move the player in their direction diff --git a/src/player.rs b/src/player.rs index 0d30883..995840f 100644 --- a/src/player.rs +++ b/src/player.rs @@ -98,7 +98,7 @@ impl Player { } /// Try to attack with the stun gun - pub fn begin_attack(&mut self, world: &mut World) { + pub fn begin_attack(&mut self, world: &mut World, current_time: f64) { if self.inventory.stun_gun.is_some() && !self.is_stunned() { self.attacking_timer = self.inventory.stun_gun.as_ref().unwrap().duration; @@ -107,7 +107,12 @@ impl Player { for jellyfish in world.jellyfish.iter_mut() { if jellyfish.position.distance_to(self.position).abs() <= stun_reach { - jellyfish.handle_getting_attacked(self.attacking_timer); + jellyfish.handle_getting_attacked(self.attacking_timer, current_time); + } + } + for octopus in world.octopus.iter_mut() { + if octopus.current_position.distance_to(self.position).abs() <= stun_reach { + octopus.handle_getting_attacked(self.attacking_timer, current_time); } } } From 3d29a971cf89443db3329117354b71d506d738c9 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 20:33:28 -0400 Subject: [PATCH 27/32] octobreath --- src/entities/enemy/octopus.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/entities/enemy/octopus.rs b/src/entities/enemy/octopus.rs index f553e97..895d8c8 100644 --- a/src/entities/enemy/octopus.rs +++ b/src/entities/enemy/octopus.rs @@ -1,8 +1,4 @@ -use crate::{ - lib::utils::calculate_linear_slide, - pallette::{TRANSLUCENT_RED_64, TRANSLUCENT_WHITE_64}, - player::Player, -}; +use crate::{lib::utils::calculate_linear_slide, pallette::{TRANSLUCENT_RED_64, TRANSLUCENT_WHITE_128, TRANSLUCENT_WHITE_64}, player::Player}; use super::base::EnemyBase; use rand::{prelude::ThreadRng, Rng}; @@ -12,6 +8,7 @@ use serde::{Deserialize, Serialize}; const OCTOPUS_SUCK_AIR_DELAY: f64 = 3.5; const OCTOPUS_SUCK_AIR_RANGE: f32 = 70.0; const OCTOPUS_SUCK_AIR_DURATION: f64 = 1.0; +const OCTOPUS_SUCK_AIR_AMOUNT: f32 = 0.1; // const RNG: ThreadRng = rand::thread_rng(); #[derive(Debug, Serialize, Deserialize, Default, Clone)] @@ -37,6 +34,8 @@ pub struct Octopus { pub suck_air_time_remaining: f64, #[serde(skip)] suck_air_bubbles: Vec, + #[serde(skip)] + has_taken_air_from_player: bool, } impl Octopus {} @@ -71,12 +70,15 @@ impl EnemyBase for Octopus { } // Every once in a while, start sucking air - if (context_2d.get_time() % OCTOPUS_SUCK_AIR_DELAY) < 0.1 { + if (context_2d.get_time() % OCTOPUS_SUCK_AIR_DELAY) < 0.1 + && self.suck_air_time_remaining == 0.0 && !is_octopus_stunned + { self.suck_air_time_remaining = OCTOPUS_SUCK_AIR_DURATION; + self.has_taken_air_from_player = false; // Spawn a few air bubbles if the player is in range if player.position.distance_to(self.current_position).abs() <= OCTOPUS_SUCK_AIR_RANGE { - for _ in 0..3 { + for _ in 0..5 { self.suck_air_bubbles.push(OctopusAirBubble { position: player.position, speed: rand::thread_rng().gen_range(0.8..1.3), @@ -93,14 +95,14 @@ impl EnemyBase for Octopus { let direction = (self.current_position - bubble.position).normalized(); // Render the bubble - context_2d.draw_circle_v(bubble.position, 2.0, TRANSLUCENT_WHITE_64); + context_2d.draw_circle_v(bubble.position, 2.0, TRANSLUCENT_WHITE_128); // Move the bubble bubble.position += direction * bubble.speed; } // Reduce time - self.suck_air_time_remaining -= dt; + self.suck_air_time_remaining = (self.suck_air_time_remaining - dt).max(0.0); } else { self.suck_air_bubbles.clear(); } @@ -110,7 +112,16 @@ impl EnemyBase for Octopus { } fn handle_logic(&mut self, player: &mut crate::player::Player, dt: f64) { - if self.suck_air_time_remaining > 0.0 {} + if self.suck_air_time_remaining > 0.0 && !self.has_taken_air_from_player { + if player.position.distance_to(self.current_position).abs() <= OCTOPUS_SUCK_AIR_RANGE { + // Take air from the player + println!("Stealing"); + player.breath_percent -= OCTOPUS_SUCK_AIR_AMOUNT; + + // Set the flag + self.has_taken_air_from_player = true; + } + } } fn handle_getting_attacked(&mut self, stun_duration: f64, current_time: f64) { From d972238574bd3dacaf2f861396d2964220e278f3 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 20:49:35 -0400 Subject: [PATCH 28/32] Main menu button fixes --- src/logic/mainmenu.rs | 47 ++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/logic/mainmenu.rs b/src/logic/mainmenu.rs index e3fc664..e03d098 100644 --- a/src/logic/mainmenu.rs +++ b/src/logic/mainmenu.rs @@ -1,9 +1,6 @@ use raylib::prelude::*; -use crate::{ - gamecore::{GameCore, GameState}, - lib::wrappers::audio::player::AudioPlayer, -}; +use crate::{gamecore::{GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer, pallette::WATER_DARK}; use super::screen::Screen; @@ -34,40 +31,48 @@ impl Screen for MainMenuScreen { draw_handle.draw_text( "ONE BREATH", (win_height / 2) - 80, - win_width / 4, - 40, + win_width / 8, + 80, Color::BLACK, ); + // Get mouse position data + let mouse_position = draw_handle.get_mouse_position(); + let hovering_play_button = mouse_position.y > (win_width as f32 / 4.0) + && mouse_position.y < (win_width as f32 / 4.0) + 60.0; + let hovering_quit_button = mouse_position.y > (win_width as f32 / 4.0) + 100.0 + && mouse_position.y < (win_width as f32 / 4.0) + 160.0; + // Play and quit draw_handle.draw_text( "Play", - (win_height / 2) - 80, - (win_width / 4) + 100, - 20, - Color::BLACK, + (win_height / 2) + 120, + (win_width / 4), + 60, + match hovering_play_button { + true => Color::GREEN, + false => Color::BLACK, + }, ); draw_handle.draw_text( "Quit", - (win_height / 2) - 80, - (win_width / 4) + 140, - 20, - Color::BLACK, + (win_height / 2) + 130, + (win_width / 4) + 100, + 60, + match hovering_quit_button { + true => Color::GREEN, + false => Color::BLACK, + }, ); // Handle button presses - let mouse_position = draw_handle.get_mouse_position(); let mouse_clicked = draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON); // Check clicks if mouse_clicked { - if mouse_position.y > (win_width as f32 / 4.0) + 100.0 - && mouse_position.y < (win_width as f32 / 4.0) + 120.0 - { + if hovering_play_button { return Some(GameState::InGame); - } else if mouse_position.y > (win_width as f32 / 4.0) + 140.0 - && mouse_position.y < (win_width as f32 / 4.0) + 180.0 - { + } else if hovering_quit_button { return Some(GameState::GameQuit); } } From 43bcfe8623519e104f6008dbfc831fa015581c15 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 21:15:57 -0400 Subject: [PATCH 29/32] darkness is taking over --- assets/worlds/mainworld.json | 4 ++-- src/items.rs | 12 +++++++++- src/logic/ingame/mod.rs | 43 ++++++++++++++++++++++++++++++++++++ src/player.rs | 1 + 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/assets/worlds/mainworld.json b/assets/worlds/mainworld.json index 4be5d70..add0aa3 100644 --- a/assets/worlds/mainworld.json +++ b/assets/worlds/mainworld.json @@ -1,7 +1,7 @@ { "end_position": { - "x": 10000.0, - "y": 10000.0 + "x": 350.0, + "y": 2100.0 }, "player_spawn": { "x": 220.0, diff --git a/src/items.rs b/src/items.rs index 90ae94d..81515c9 100644 --- a/src/items.rs +++ b/src/items.rs @@ -25,7 +25,17 @@ impl StunGun { pub struct AirBag; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -pub struct Flashlight; +pub struct Flashlight { + pub radius: f32 +} + +impl Flashlight { + pub fn lvl1() -> Self { + Self { + radius: 120.0 + } + } +} #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Flippers { diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index 2cc67f9..b6e2140 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -87,6 +87,46 @@ impl InGameScreen { context_2d.draw_rectangle_lines_ex(collider, 1, Color::RED); } } + + fn render_darkness(&mut self, draw_handle: &mut RaylibDrawHandle, game_core: &mut GameCore) { + // Calculate the min view radius based on the current flashlight + let mut min_radius = 0.0; + if game_core.player.inventory.flashlight.is_some() { + min_radius = game_core + .player + .inventory + .flashlight + .as_ref() + .unwrap() + .radius; + } + + // Get the window center + let win_height = draw_handle.get_screen_height(); + let win_width = draw_handle.get_screen_width(); + let window_center = Vector2 { + x: (win_width as f32 / 2.0), + y: (win_height as f32 / 2.0), + }; + + // Calculate the occusion radius based on depth + let radius = (win_width as f32 + * (1.0 + - (game_core.player.calculate_depth_percent(&game_core.world) * 1.3) + .clamp(0.0, 1.0))) + .max(min_radius); + + // Render the overlay + draw_handle.draw_ring( + window_center, + radius, + win_width as f32, + 0, + 360, + 128, + Color::BLACK, + ); + } } impl Screen for InGameScreen { @@ -156,6 +196,9 @@ impl Screen for InGameScreen { .render(&mut context_2d, &mut game_core.resources, dt); } + // Render the darkness layer + self.render_darkness(draw_handle, game_core); + // Render the hud hud::render_hud(draw_handle, game_core, window_center); diff --git a/src/player.rs b/src/player.rs index 995840f..e7f367e 100644 --- a/src/player.rs +++ b/src/player.rs @@ -25,6 +25,7 @@ impl PlayerInventory { pub fn new() -> Self { Self { stun_gun: Some(StunGun::lvl1()), //TMP + flashlight: Some(Flashlight::lvl1()), //TMP ..Default::default() } } From 0c4928b892a5e7020cc00ea8c42920c0a9dbb046 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 21:45:54 -0400 Subject: [PATCH 30/32] dark png --- assets/img/map/darkness.png | Bin 0 -> 31831 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/img/map/darkness.png diff --git a/assets/img/map/darkness.png b/assets/img/map/darkness.png new file mode 100644 index 0000000000000000000000000000000000000000..bca5ae68f31116adda44a78d9016bd8e80a3e5fa GIT binary patch literal 31831 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 Date: Sun, 25 Apr 2021 09:23:31 -0400 Subject: [PATCH 31/32] proper background rendering with scale --- src/logic/ingame/mod.rs | 46 ++++++++++++++++++++++++++++++++++------- src/resources.rs | 7 +++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index b6e2140..5e161f7 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -116,16 +116,46 @@ impl InGameScreen { .clamp(0.0, 1.0))) .max(min_radius); + // Determine width and height scales + let width_scale = 5.0; + let height_scale = 5.0; + + // Get the base sizes of everything + let texture_width = game_core.resources.darkness_overlay.width as f32; + let texture_height = game_core.resources.darkness_overlay.height as f32; + let texture_width_scaled = texture_width * width_scale; + let texture_height_scaled = texture_height * height_scale; + // Render the overlay - draw_handle.draw_ring( - window_center, - radius, - win_width as f32, - 0, - 360, - 128, - Color::BLACK, + draw_handle.draw_texture_pro( + &game_core.resources.darkness_overlay, + Rectangle { + x: 0.0, + y: 0.0, + width:texture_width, + height: texture_height, + }, + Rectangle { + x: (win_width as f32 - texture_width_scaled) / 2.0, + y: (win_height as f32 - texture_height_scaled) / 2.0, + width: texture_width_scaled, + height: texture_height_scaled, + }, + Vector2 { x: 0.0, y: 0.0 }, + 0.0, + Color::WHITE, ); + + // Render the overlay + // draw_handle.draw_ring( + // window_center, + // radius, + // win_width as f32, + // 0, + // 360, + // 128, + // Color::BLACK, + // ); } } diff --git a/src/resources.rs b/src/resources.rs index 51d115c..03cd78c 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -24,6 +24,9 @@ pub struct GlobalResources { // Enemies pub jellyfish_animation_regular: FrameAnimationWrapper, pub jellyfish_animation_attack: FrameAnimationWrapper, + + // Darkness layer + pub darkness_overlay: Texture2D } impl GlobalResources { @@ -95,6 +98,10 @@ impl GlobalResources { 15, 4, ), + darkness_overlay: raylib.load_texture_from_image( + &thread, + &Image::load_image("./assets/img/map/darkness.png")?, + )?, }) } } From 8b15d8fcaf5c4ec28691b047cb54372f701c6be4 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sun, 25 Apr 2021 09:33:32 -0400 Subject: [PATCH 32/32] Better darkness logic --- assets/img/map/darkness.png | Bin 31831 -> 50768 bytes src/items.rs | 2 +- src/logic/ingame/mod.rs | 28 ++++++---------------------- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/assets/img/map/darkness.png b/assets/img/map/darkness.png index bca5ae68f31116adda44a78d9016bd8e80a3e5fa..33450f0df3c44acb6e9d1586d30a6db610207034 100644 GIT binary patch literal 50768 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�WOVe~a$5TLw8@xB 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 z%0A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 Self { Self { - radius: 120.0 + radius: 0.25 } } } diff --git a/src/logic/ingame/mod.rs b/src/logic/ingame/mod.rs index 5e161f7..3f2bce6 100644 --- a/src/logic/ingame/mod.rs +++ b/src/logic/ingame/mod.rs @@ -104,21 +104,16 @@ impl InGameScreen { // Get the window center let win_height = draw_handle.get_screen_height(); let win_width = draw_handle.get_screen_width(); - let window_center = Vector2 { - x: (win_width as f32 / 2.0), - y: (win_height as f32 / 2.0), - }; // Calculate the occusion radius based on depth - let radius = (win_width as f32 - * (1.0 - - (game_core.player.calculate_depth_percent(&game_core.world) * 1.3) - .clamp(0.0, 1.0))) + let radius = (1.0 + - (game_core.player.calculate_depth_percent(&game_core.world) * 1.3).clamp(0.0, 1.0)) .max(min_radius); // Determine width and height scales - let width_scale = 5.0; - let height_scale = 5.0; + // This is clamped to make the rendering logic below easier by removing the need to overdraw + let width_scale = (5.0 * radius).max(0.5); + let height_scale = (5.0 * radius).max(0.5); // Get the base sizes of everything let texture_width = game_core.resources.darkness_overlay.width as f32; @@ -132,7 +127,7 @@ impl InGameScreen { Rectangle { x: 0.0, y: 0.0, - width:texture_width, + width: texture_width, height: texture_height, }, Rectangle { @@ -145,17 +140,6 @@ impl InGameScreen { 0.0, Color::WHITE, ); - - // Render the overlay - // draw_handle.draw_ring( - // window_center, - // radius, - // win_width as f32, - // 0, - // 360, - // 128, - // Color::BLACK, - // ); } }