From 1585521fe31b7e9e69d4a1c3d81ee721cb8604d0 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 24 Apr 2021 19:09:28 -0400 Subject: [PATCH 1/4] 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 2/4] 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 3/4] 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 4/4] 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) {