Merge branch 'ouchies' into shop
This commit is contained in:
commit
315a487cdf
File diff suppressed because one or more lines are too long
7
src/entities/enemy/base.rs
Normal file
7
src/entities/enemy/base.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
pub trait EnemyBase {
|
||||||
|
fn render();
|
||||||
|
fn handle_logic();
|
||||||
|
fn handle_getting_attacked();
|
||||||
|
}
|
1
src/entities/enemy/mod.rs
Normal file
1
src/entities/enemy/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod base;
|
@ -1,30 +1,51 @@
|
|||||||
use rand::{Rng, prelude::ThreadRng};
|
use rand::{prelude::ThreadRng, Rng};
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
|
|
||||||
use crate::{gamecore::GameCore, lib::utils::triangles::rotate_vector, player::Player, world::World};
|
use crate::{
|
||||||
|
gamecore::GameCore, lib::utils::triangles::rotate_vector, player::Player, world::World,
|
||||||
|
};
|
||||||
|
|
||||||
const FISH_FOLLOW_PLAYER_DISTANCE: f32 = 30.0;
|
const FISH_FOLLOW_PLAYER_DISTANCE: f32 = 30.0;
|
||||||
const FISH_FOLLOW_PLAYER_SPEED: f32 = 1.8;
|
const FISH_FOLLOW_PLAYER_SPEED: f32 = 1.8;
|
||||||
const FISH_FOLLOW_PLAYER_SPEED_FAST: f32 = FISH_FOLLOW_PLAYER_SPEED * 3.0;
|
const FISH_FOLLOW_PLAYER_SPEED_FAST: f32 = FISH_FOLLOW_PLAYER_SPEED * 3.0;
|
||||||
const FISH_ATTACH_RADIUS: f32 = 20.0;
|
const FISH_ATTACH_RADIUS: f32 = 20.0;
|
||||||
|
|
||||||
|
const FISH_VISION: f32 = 25.0;
|
||||||
|
const FISH_MAX_SPEED: f32 = 2.0;
|
||||||
|
const FISH_MAX_FORCE: f32 = 0.05;
|
||||||
|
const FISH_FACTOR_ATTRACTION: f32 = 1.0;
|
||||||
|
const FISH_FACTOR_PLAYER: f32 = 0.1;
|
||||||
|
const FISH_FACTOR_COHESION: f32 = 0.1;
|
||||||
|
const FISH_SEPARATION_DISTANCE: f32 = 15.0;
|
||||||
|
const FISH_FACTOR_SEPARATION: f32 = 1.5;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FishEntity {
|
pub struct FishEntity {
|
||||||
position: Vector2,
|
position: Vector2,
|
||||||
direction: Vector2,
|
direction: Vector2,
|
||||||
|
velocity: Vector2,
|
||||||
pub following_player: bool,
|
pub following_player: bool,
|
||||||
size: Vector2,
|
size: Vector2,
|
||||||
rng: ThreadRng
|
rng: ThreadRng,
|
||||||
|
color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FishEntity {
|
impl FishEntity {
|
||||||
pub fn new(position: Vector2) -> Self {
|
pub fn new(position: Vector2) -> Self {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
Self {
|
Self {
|
||||||
position: position,
|
position: position,
|
||||||
direction: Vector2::zero(),
|
direction: Vector2::zero(),
|
||||||
|
velocity: Vector2::zero(),
|
||||||
following_player: false,
|
following_player: false,
|
||||||
size: Vector2 { x: 5.0, y: 8.0 },
|
size: Vector2 { x: 5.0, y: 8.0 },
|
||||||
rng: rand::thread_rng()
|
color: Color {
|
||||||
|
r: rng.gen_range(128..225),
|
||||||
|
g: rng.gen_range(128..225),
|
||||||
|
b: rng.gen_range(128..225),
|
||||||
|
a: 140,
|
||||||
|
},
|
||||||
|
rng,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,33 +58,80 @@ impl FishEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_follow_player(&mut self, player: &Player, dt: f64, other_fish: &Vec<FishEntity>) {
|
pub fn handle_follow_player(&mut self, player: &Player, dt: f64, other_fish: &Vec<FishEntity>) {
|
||||||
// Distance and direction to player
|
let mut acceleration: Vector2 = Vector2::zero();
|
||||||
let dist_to_player = player.position - self.position;
|
|
||||||
let dist_to_player_lin = self.position.distance_to(player.position);
|
|
||||||
let mut direction_to_player = dist_to_player;
|
|
||||||
direction_to_player.normalize();
|
|
||||||
|
|
||||||
// Fish movement
|
let mut steer: Vector2 = Vector2::zero();
|
||||||
let movement;
|
let mut count1: u16 = 0;
|
||||||
|
let mut sum1: Vector2 = Vector2::zero();
|
||||||
// Random variance
|
let mut count2: u16 = 0;
|
||||||
let variance = self.rng.gen_range(500.0..1000.0) / 1000.0;
|
let mut sum2: Vector2 = Vector2::zero();
|
||||||
|
let mut count3: u16 = 0;
|
||||||
// If the fish is double its follow distance from the player
|
// separation
|
||||||
if dist_to_player_lin.abs() > (FISH_FOLLOW_PLAYER_DISTANCE * 2.0) {
|
for i in other_fish {
|
||||||
movement = direction_to_player * FISH_FOLLOW_PLAYER_SPEED_FAST * variance;
|
let dist = (self.position - i.position).length();
|
||||||
} else {
|
if dist < FISH_SEPARATION_DISTANCE && dist > 0.0 {
|
||||||
// Move slowly in the direction of the player unless too close
|
let mut diff: Vector2 = self.position - i.position;
|
||||||
if dist_to_player_lin.abs() > FISH_FOLLOW_PLAYER_DISTANCE {
|
diff.normalize();
|
||||||
movement = direction_to_player * FISH_FOLLOW_PLAYER_SPEED * variance;
|
diff /= dist;
|
||||||
} else {
|
steer += diff;
|
||||||
movement = Vector2::zero();
|
count1 += 1;
|
||||||
|
}
|
||||||
|
if dist < FISH_VISION && dist > 0.0 {
|
||||||
|
sum1 += i.direction;
|
||||||
|
count2 += 1;
|
||||||
|
sum2 += i.position;
|
||||||
|
count3 += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if count1 > 0 {
|
||||||
|
steer /= count1 as f32;
|
||||||
|
}
|
||||||
|
if steer.x != 0.0 || steer.y != 0.0 {
|
||||||
|
steer.normalize();
|
||||||
|
steer *= FISH_MAX_SPEED;
|
||||||
|
steer -= self.velocity;
|
||||||
|
steer.x = f32::min(f32::max(steer.x, -FISH_MAX_FORCE), FISH_MAX_FORCE);
|
||||||
|
steer.y = f32::min(f32::max(steer.y, -FISH_MAX_FORCE), FISH_MAX_FORCE);
|
||||||
|
acceleration += steer * FISH_FACTOR_SEPARATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// attraction
|
||||||
|
if count2 > 0 {
|
||||||
|
sum1 /= count2 as f32;
|
||||||
|
sum1.normalize();
|
||||||
|
sum1 *= FISH_MAX_SPEED;
|
||||||
|
sum1 -= self.velocity;
|
||||||
|
sum1.x = f32::min(f32::max(sum1.x, -FISH_MAX_FORCE), FISH_MAX_FORCE);
|
||||||
|
sum1.y = f32::min(f32::max(sum1.y, -FISH_MAX_FORCE), FISH_MAX_FORCE);
|
||||||
|
acceleration += sum1 * FISH_FACTOR_ATTRACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cohesion
|
||||||
|
if count3 > 0 {
|
||||||
|
sum2 /= count3 as f32;
|
||||||
|
let mut desired: Vector2 = sum2 - self.position;
|
||||||
|
|
||||||
|
desired.normalize();
|
||||||
|
desired *= FISH_MAX_SPEED;
|
||||||
|
|
||||||
|
desired.x = f32::min(f32::max(desired.x, -FISH_MAX_FORCE), FISH_MAX_FORCE);
|
||||||
|
desired.y = f32::min(f32::max(desired.y, -FISH_MAX_FORCE), FISH_MAX_FORCE);
|
||||||
|
|
||||||
|
acceleration += desired * FISH_FACTOR_COHESION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn to player
|
||||||
|
let mut player_factor: Vector2 = player.position - self.position;
|
||||||
|
player_factor.normalize();
|
||||||
|
acceleration += player_factor * FISH_FACTOR_PLAYER;
|
||||||
|
|
||||||
// Move the fish
|
// Move the fish
|
||||||
self.direction = direction_to_player;
|
self.direction = self.velocity.normalized();
|
||||||
self.position += movement;
|
self.velocity += acceleration;
|
||||||
|
|
||||||
|
self.velocity.x = f32::min(f32::max(self.velocity.x, -FISH_MAX_SPEED), FISH_MAX_SPEED);
|
||||||
|
self.velocity.y = f32::min(f32::max(self.velocity.y, -FISH_MAX_SPEED), FISH_MAX_SPEED);
|
||||||
|
self.position += self.velocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_free_movement(&mut self, player: &mut Player, dt: f64) {
|
pub fn handle_free_movement(&mut self, player: &mut Player, dt: f64) {
|
||||||
@ -76,6 +144,7 @@ impl FishEntity {
|
|||||||
// Handle player picking up fish
|
// Handle player picking up fish
|
||||||
if player.position.distance_to(self.position).abs() <= player.size.y * 2.2 {
|
if player.position.distance_to(self.position).abs() <= player.size.y * 2.2 {
|
||||||
self.following_player = true;
|
self.following_player = true;
|
||||||
|
self.velocity = self.direction.normalized();
|
||||||
|
|
||||||
// Add currency to the player
|
// Add currency to the player
|
||||||
player.coins += 1;
|
player.coins += 1;
|
||||||
@ -127,7 +196,7 @@ impl FishEntity {
|
|||||||
self.position + fish_front,
|
self.position + fish_front,
|
||||||
self.position + fish_bl,
|
self.position + fish_bl,
|
||||||
self.position + fish_br,
|
self.position + fish_br,
|
||||||
Color::BLACK,
|
self.color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,2 @@
|
|||||||
pub mod fish;
|
pub mod fish;
|
||||||
|
pub mod enemy;
|
@ -6,7 +6,11 @@ use raylib::{
|
|||||||
camera::Camera2D, math::Vector2, prelude::RaylibDrawHandle, RaylibHandle, RaylibThread,
|
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 failure::Error;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
@ -31,10 +35,10 @@ impl fmt::Display for GameState {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
pub struct GameProgress {
|
pub struct GameProgress {
|
||||||
coins: u32,
|
pub coins: u32,
|
||||||
max_depth: f32,
|
pub max_depth: f32,
|
||||||
fastest_time: Option<f64>,
|
pub fastest_time: Option<f64>,
|
||||||
inventory: Vec<ShopItems>,
|
pub inventory: PlayerInventory,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameProgress {
|
impl GameProgress {
|
||||||
|
28
src/items.rs
28
src/items.rs
@ -1,4 +1,26 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AirBag;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Flashlight;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
#[serde(tag = "t", content = "c")]
|
#[serde(tag = "t", content = "c")]
|
||||||
@ -53,5 +75,7 @@ impl ShopItems{
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
pub struct Flippers {
|
||||||
|
pub speed_increase: f32
|
||||||
}
|
}
|
@ -1,2 +1,12 @@
|
|||||||
pub mod profiler;
|
pub mod profiler;
|
||||||
pub mod triangles;
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
use raylib::math::Vector2;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use serialstudio::{
|
use serialstudio::{
|
||||||
data::{DataGroup, DataSet, TelemetryFrame},
|
data::{DataGroup, DataSet, TelemetryFrame},
|
||||||
@ -21,7 +22,8 @@ pub struct ProfilerData {
|
|||||||
// Player
|
// Player
|
||||||
pub player_coins: u32,
|
pub player_coins: u32,
|
||||||
pub player_boost_percent: f32,
|
pub player_boost_percent: f32,
|
||||||
pub player_breath_percent: f32
|
pub player_breath_percent: f32,
|
||||||
|
pub player_pose: Vector2
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The development profiler
|
/// The development profiler
|
||||||
@ -147,6 +149,20 @@ impl GameProfiler {
|
|||||||
unit: Some("%".to_string()),
|
unit: Some("%".to_string()),
|
||||||
w_type: None,
|
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,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -8,13 +8,7 @@ pub fn render_hud(
|
|||||||
window_center: Vector2,
|
window_center: Vector2,
|
||||||
) {
|
) {
|
||||||
// Get the relevant data
|
// Get the relevant data
|
||||||
let dist_from_player_to_end = game_core
|
let progress = game_core.player.calculate_depth_percent(&game_core.world);
|
||||||
.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);
|
|
||||||
|
|
||||||
// Determine the progress slider position
|
// Determine the progress slider position
|
||||||
let slider_bound_height = 20.0;
|
let slider_bound_height = 20.0;
|
||||||
|
@ -147,7 +147,8 @@ impl Screen for InGameScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render Player
|
// 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, dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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_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);
|
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
|
// Move the player in their direction
|
||||||
let speed_multiplier;
|
let speed_multiplier;
|
||||||
if user_request_boost && game_core.player.boost_percent >= 0.0 {
|
if user_request_boost && game_core.player.boost_percent >= 0.0 {
|
||||||
@ -128,7 +132,8 @@ pub fn update_player_movement(
|
|||||||
|
|
||||||
// Only do this if the mouse is far enough away
|
// Only do this if the mouse is far enough away
|
||||||
let player_real_movement = game_core.player.direction * speed_multiplier;
|
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.is_moving = true;
|
||||||
game_core.player.position += player_real_movement;
|
game_core.player.position += player_real_movement;
|
||||||
|
|
||||||
@ -145,6 +150,11 @@ pub fn update_player_movement(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
game_core.player.is_moving = false;
|
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
|
// Move the camera to follow the player
|
||||||
@ -169,60 +179,3 @@ pub fn update_player_movement(
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_player(context_2d: &mut RaylibMode2D<RaylibDrawHandle>, 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{gamecore::{GameCore, GameState}, lib::{utils::calculate_linear_slide, wrappers::audio::player::AudioPlayer}};
|
||||||
gamecore::{GameCore, GameState},
|
|
||||||
lib::wrappers::audio::player::AudioPlayer,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::screen::Screen;
|
use super::screen::Screen;
|
||||||
|
|
||||||
@ -32,14 +29,7 @@ impl LoadingScreen {
|
|||||||
|
|
||||||
fn get_logo_mask(&self, playthrough_percent: f64) -> Color {
|
fn get_logo_mask(&self, playthrough_percent: f64) -> Color {
|
||||||
// Determine the alpha
|
// Determine the alpha
|
||||||
let alpha;
|
let alpha = calculate_linear_slide(playthrough_percent);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a color mask
|
// Build a color mask
|
||||||
Color {
|
Color {
|
||||||
|
@ -134,6 +134,7 @@ fn main() {
|
|||||||
profiler.data.player_coins = game_core.player.coins;
|
profiler.data.player_coins = game_core.player.coins;
|
||||||
profiler.data.player_boost_percent = game_core.player.boost_percent;
|
profiler.data.player_boost_percent = game_core.player.boost_percent;
|
||||||
profiler.data.player_breath_percent = game_core.player.breath_percent;
|
profiler.data.player_breath_percent = game_core.player.breath_percent;
|
||||||
|
profiler.data.player_pose = game_core.player.position;
|
||||||
|
|
||||||
// Send telemetry data
|
// Send telemetry data
|
||||||
profiler.update();
|
profiler.update();
|
||||||
|
123
src/player.rs
123
src/player.rs
@ -1,6 +1,17 @@
|
|||||||
use raylib::math::{Rectangle, Vector2};
|
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::lib::utils::triangles::rotate_vector;
|
const AOE_RING_MAX_RADIUS: f32 = 60.0;
|
||||||
|
const STUN_ATTACK_TIME: f64 = 0.75;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
pub struct PlayerInventory {
|
||||||
|
stun_gun: Option<StunGun>,
|
||||||
|
air_bag: Option<AirBag>,
|
||||||
|
flashlight: Option<Flashlight>,
|
||||||
|
flippers: Option<Flippers>
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
@ -13,6 +24,9 @@ pub struct Player {
|
|||||||
pub is_moving: bool,
|
pub is_moving: bool,
|
||||||
pub is_boosting: bool,
|
pub is_boosting: bool,
|
||||||
pub is_boost_charging: bool,
|
pub is_boost_charging: bool,
|
||||||
|
pub inventory: PlayerInventory,
|
||||||
|
pub stun_timer: f64,
|
||||||
|
pub attacking_timer: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
@ -56,4 +70,109 @@ impl Player {
|
|||||||
|
|
||||||
return rectangle.check_collision_circle_rec(self.position, (self.size.y * 0.5) / 2.0);
|
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 true || self.inventory.stun_gun.is_some() && self.stun_timer == 0.0 {
|
||||||
|
self.attacking_timer = self.inventory.stun_gun.as_ref().unwrap().duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(
|
||||||
|
&mut self,
|
||||||
|
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||||
|
resources: &mut GlobalResources,
|
||||||
|
dt: f64,
|
||||||
|
) {
|
||||||
|
// 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,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate AOE ring
|
||||||
|
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 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user