commit
ff36b1c2c3
@ -17,5 +17,17 @@
|
|||||||
"y": 100
|
"y": 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"octopus": [
|
||||||
|
{
|
||||||
|
"position_a" : {
|
||||||
|
"x": 300,
|
||||||
|
"y": 150
|
||||||
|
},
|
||||||
|
"position_b" : {
|
||||||
|
"x": 500,
|
||||||
|
"y": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -6,9 +6,10 @@ pub trait EnemyBase {
|
|||||||
fn render(
|
fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||||
|
player: &mut Player,
|
||||||
resources: &mut GlobalResources,
|
resources: &mut GlobalResources,
|
||||||
dt: f64,
|
dt: f64,
|
||||||
);
|
);
|
||||||
fn handle_logic(&mut self, player: &mut Player, 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);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ impl EnemyBase for JellyFish {
|
|||||||
fn render(
|
fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
context_2d: &mut raylib::prelude::RaylibMode2D<raylib::prelude::RaylibDrawHandle>,
|
context_2d: &mut raylib::prelude::RaylibMode2D<raylib::prelude::RaylibDrawHandle>,
|
||||||
|
player: &mut Player,
|
||||||
resources: &mut GlobalResources,
|
resources: &mut GlobalResources,
|
||||||
dt: f64,
|
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.stunned_timer = stun_duration;
|
||||||
self.max_stunned_time = stun_duration;
|
self.max_stunned_time = stun_duration;
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
pub mod base;
|
pub mod base;
|
||||||
pub mod jellyfish;
|
pub mod jellyfish;
|
||||||
|
pub mod octopus;
|
131
src/entities/enemy/octopus.rs
Normal file
131
src/entities/enemy/octopus.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
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};
|
||||||
|
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 OCTOPUS_SUCK_AIR_AMOUNT: f32 = 0.1;
|
||||||
|
// 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,
|
||||||
|
pub position_b: Vector2,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub current_position: Vector2,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
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<OctopusAirBubble>,
|
||||||
|
#[serde(skip)]
|
||||||
|
has_taken_air_from_player: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Octopus {}
|
||||||
|
|
||||||
|
impl EnemyBase for Octopus {
|
||||||
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
context_2d: &mut raylib::prelude::RaylibMode2D<raylib::prelude::RaylibDrawHandle>,
|
||||||
|
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 = (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;
|
||||||
|
|
||||||
|
// 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 == 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..5 {
|
||||||
|
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_128);
|
||||||
|
|
||||||
|
// Move the bubble
|
||||||
|
bubble.position += direction * bubble.speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce time
|
||||||
|
self.suck_air_time_remaining = (self.suck_air_time_remaining - dt).max(0.0);
|
||||||
|
} 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) {
|
||||||
|
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) {
|
||||||
|
self.stunned_timer = stun_duration;
|
||||||
|
self.max_stunned_time = stun_duration;
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
entities::enemy::base::EnemyBase,
|
entities::enemy::base::EnemyBase,
|
||||||
gamecore::{GameCore, GameState},
|
gamecore::{GameCore, GameState},
|
||||||
lib::wrappers::audio::player::AudioPlayer,
|
lib::wrappers::audio::player::AudioPlayer,
|
||||||
pallette::{SKY, WATER},
|
pallette::{SKY, WATER, WATER_DARK},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::screen::Screen;
|
use super::screen::Screen;
|
||||||
@ -32,7 +32,7 @@ impl InGameScreen {
|
|||||||
&mut self,
|
&mut self,
|
||||||
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||||
game_core: &mut GameCore,
|
game_core: &mut GameCore,
|
||||||
dt: f64
|
dt: f64,
|
||||||
) {
|
) {
|
||||||
// Build source bounds
|
// Build source bounds
|
||||||
let source_bounds = Rectangle {
|
let source_bounds = Rectangle {
|
||||||
@ -49,7 +49,14 @@ impl InGameScreen {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Clear the background
|
// 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
|
// Render fish
|
||||||
let fish_clone = game_core.world.fish.clone();
|
let fish_clone = game_core.world.fish.clone();
|
||||||
@ -126,7 +133,21 @@ impl Screen for InGameScreen {
|
|||||||
// Render entities
|
// Render entities
|
||||||
for jellyfish in game_core.world.jellyfish.iter_mut() {
|
for jellyfish in game_core.world.jellyfish.iter_mut() {
|
||||||
jellyfish.handle_logic(&mut game_core.player, dt);
|
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.player,
|
||||||
|
&mut game_core.resources,
|
||||||
|
dt,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render Player
|
// Render Player
|
||||||
|
@ -79,7 +79,7 @@ pub fn update_player_movement(
|
|||||||
let user_request_action = draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON);
|
let user_request_action = draw_handle.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON);
|
||||||
|
|
||||||
if user_request_action {
|
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
|
// Move the player in their direction
|
||||||
|
@ -35,6 +35,13 @@ pub const WATER: Color = Color {
|
|||||||
a: 255
|
a: 255
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const WATER_DARK: Color = Color {
|
||||||
|
r: 8,
|
||||||
|
g: 24,
|
||||||
|
b: 54,
|
||||||
|
a: 255
|
||||||
|
};
|
||||||
|
|
||||||
pub const TRANSLUCENT_RED_64: Color = Color {
|
pub const TRANSLUCENT_RED_64: Color = Color {
|
||||||
r: 230,
|
r: 230,
|
||||||
g: 41,
|
g: 41,
|
||||||
|
@ -98,7 +98,7 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try to attack with the stun gun
|
/// 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() {
|
if self.inventory.stun_gun.is_some() && !self.is_stunned() {
|
||||||
self.attacking_timer = self.inventory.stun_gun.as_ref().unwrap().duration;
|
self.attacking_timer = self.inventory.stun_gun.as_ref().unwrap().duration;
|
||||||
|
|
||||||
@ -107,7 +107,12 @@ impl Player {
|
|||||||
|
|
||||||
for jellyfish in world.jellyfish.iter_mut() {
|
for jellyfish in world.jellyfish.iter_mut() {
|
||||||
if jellyfish.position.distance_to(self.position).abs() <= stun_reach {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use failure::Error;
|
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)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
@ -21,7 +21,9 @@ pub struct World {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub colliders: Vec<Rectangle>,
|
pub colliders: Vec<Rectangle>,
|
||||||
|
|
||||||
pub jellyfish: Vec<JellyFish>
|
pub jellyfish: Vec<JellyFish>,
|
||||||
|
pub octopus: Vec<Octopus>,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
|
Reference in New Issue
Block a user