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); } } }