Merge remote-tracking branch 'origin/player_cleanup' into ouchies
This commit is contained in:
commit
79bae68f71
1
assets/img/map/cave.json
Normal file
1
assets/img/map/cave.json
Normal file
File diff suppressed because one or more lines are too long
BIN
assets/img/map/cave.png
Normal file
BIN
assets/img/map/cave.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 292 KiB |
Before Width: | Height: | Size: 894 B After Width: | Height: | Size: 894 B |
@ -3,6 +3,10 @@
|
|||||||
"x": 10000.0,
|
"x": 10000.0,
|
||||||
"y": 10000.0
|
"y": 10000.0
|
||||||
},
|
},
|
||||||
|
"player_spawn": {
|
||||||
|
"x": 220.0,
|
||||||
|
"y": 50.0
|
||||||
|
},
|
||||||
"fish": [
|
"fish": [
|
||||||
{
|
{
|
||||||
"x": 500.0,
|
"x": 500.0,
|
||||||
|
@ -31,10 +31,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: Vec<ShopItems>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameProgress {
|
impl GameProgress {
|
||||||
@ -44,6 +44,7 @@ impl GameProgress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn from_file(file: String) -> Result<Self, Error> {
|
pub fn from_file(file: String) -> Result<Self, Error> {
|
||||||
// Load the file
|
// Load the file
|
||||||
let file = File::open(file)?;
|
let file = File::open(file)?;
|
||||||
@ -107,6 +108,7 @@ impl GameCore {
|
|||||||
world: World,
|
world: World,
|
||||||
progress: GameProgress,
|
progress: GameProgress,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let player = Player::new(&world.player_spawn);
|
||||||
Self {
|
Self {
|
||||||
state: GameState::Loading,
|
state: GameState::Loading,
|
||||||
last_state: GameState::Loading,
|
last_state: GameState::Loading,
|
||||||
@ -117,13 +119,13 @@ impl GameCore {
|
|||||||
.expect("Failed to load game assets. Can not launch!"),
|
.expect("Failed to load game assets. Can not launch!"),
|
||||||
master_camera: Camera2D {
|
master_camera: Camera2D {
|
||||||
offset: Vector2::zero(),
|
offset: Vector2::zero(),
|
||||||
target: Vector2::zero(),
|
target: world.player_spawn,
|
||||||
rotation: 0.0,
|
rotation: 0.0,
|
||||||
zoom: 2.0,
|
zoom: 2.0,
|
||||||
},
|
},
|
||||||
show_simple_debug_info: false,
|
show_simple_debug_info: false,
|
||||||
world: world,
|
world: world,
|
||||||
player: Player::new(),
|
player,
|
||||||
progress: progress,
|
progress: progress,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -6,6 +6,7 @@ use raylib::prelude::*;
|
|||||||
use crate::{
|
use crate::{
|
||||||
gamecore::{GameCore, GameState},
|
gamecore::{GameCore, GameState},
|
||||||
lib::wrappers::audio::player::AudioPlayer,
|
lib::wrappers::audio::player::AudioPlayer,
|
||||||
|
pallette::{SKY, WATER},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::screen::Screen;
|
use super::screen::Screen;
|
||||||
@ -31,7 +32,48 @@ impl InGameScreen {
|
|||||||
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||||
game_core: &mut GameCore,
|
game_core: &mut GameCore,
|
||||||
) {
|
) {
|
||||||
context_2d.draw_circle(0, 0, 10.0, Color::BLACK);
|
// Build source bounds
|
||||||
|
let source_bounds = Rectangle {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: game_core.resources.cave_mid_layer.width as f32,
|
||||||
|
height: game_core.resources.cave_mid_layer.height as f32,
|
||||||
|
};
|
||||||
|
let world_bounds = Rectangle {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: game_core.resources.cave_mid_layer.width as f32,
|
||||||
|
height: game_core.resources.cave_mid_layer.height as f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clear the background
|
||||||
|
context_2d.draw_rectangle_rec(world_bounds, WATER);
|
||||||
|
|
||||||
|
// Render the world texture
|
||||||
|
context_2d.draw_texture_rec(
|
||||||
|
&game_core.resources.cave_mid_layer,
|
||||||
|
source_bounds,
|
||||||
|
Vector2 {
|
||||||
|
x: world_bounds.x,
|
||||||
|
y: world_bounds.y,
|
||||||
|
},
|
||||||
|
Color::WHITE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_colliders(
|
||||||
|
&mut self,
|
||||||
|
context_2d: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||||
|
game_core: &mut GameCore,
|
||||||
|
) {
|
||||||
|
// Render every collider
|
||||||
|
for collider in game_core.world.colliders.iter() {
|
||||||
|
context_2d.draw_rectangle_lines_ex(
|
||||||
|
collider,
|
||||||
|
1,
|
||||||
|
Color::RED,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +89,7 @@ impl Screen for InGameScreen {
|
|||||||
let dt = draw_handle.get_time() - game_core.last_frame_time;
|
let dt = draw_handle.get_time() - game_core.last_frame_time;
|
||||||
|
|
||||||
// Clear frame
|
// Clear frame
|
||||||
draw_handle.clear_background(Color::BLUE);
|
draw_handle.clear_background(Color::BLACK);
|
||||||
|
|
||||||
// Handle the pause menu being opened
|
// Handle the pause menu being opened
|
||||||
if draw_handle.is_key_pressed(KeyboardKey::KEY_ESCAPE) {
|
if draw_handle.is_key_pressed(KeyboardKey::KEY_ESCAPE) {
|
||||||
@ -72,6 +114,9 @@ impl Screen for InGameScreen {
|
|||||||
|
|
||||||
// Render the world
|
// Render the world
|
||||||
self.render_world(&mut context_2d, game_core);
|
self.render_world(&mut context_2d, game_core);
|
||||||
|
if game_core.show_simple_debug_info{
|
||||||
|
self.render_colliders(&mut context_2d, game_core);
|
||||||
|
}
|
||||||
|
|
||||||
// Render entities
|
// Render entities
|
||||||
let fish_clone = game_core.world.fish.clone();
|
let fish_clone = game_core.world.fish.clone();
|
||||||
@ -81,7 +126,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the hud
|
// Render the hud
|
||||||
|
@ -129,8 +129,20 @@ 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 {
|
if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 {
|
||||||
game_core.player.position += player_real_movement;
|
|
||||||
game_core.player.is_moving = true;
|
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 {
|
||||||
|
game_core.player.position -= player_real_movement;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
game_core.player.is_moving = false;
|
game_core.player.is_moving = false;
|
||||||
}
|
}
|
||||||
@ -142,65 +154,18 @@ pub fn update_player_movement(
|
|||||||
draw_handle.get_world_to_screen2D(game_core.player.position, game_core.master_camera);
|
draw_handle.get_world_to_screen2D(game_core.player.position, game_core.master_camera);
|
||||||
|
|
||||||
// Camera only moves if you get close to the edge of the screen
|
// Camera only moves if you get close to the edge of the screen
|
||||||
if player_screen_position.distance_to(window_center).abs() > (window_center.y - 40.0) {
|
if player_screen_position.distance_to(window_center).abs() > 100.0 {
|
||||||
game_core.master_camera.target += player_real_movement;
|
game_core.master_camera.target += player_real_movement;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_player(context_2d: &mut RaylibMode2D<RaylibDrawHandle>, game_core: &mut GameCore) {
|
// If the player is not on screen, snap the camera to them
|
||||||
// Get the player
|
if player_screen_position.distance_to(window_center).abs() > window_center.y {
|
||||||
let player = &game_core.player;
|
game_core.master_camera.target = game_core.player.position - (window_center / 2.0);
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // Clamp camera target y to 0
|
||||||
|
// if game_core.master_camera.target.y < -100.0 {
|
||||||
|
// game_core.master_camera.target.y = -100.0;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::screen::Screen;
|
use super::screen::Screen;
|
||||||
|
|
||||||
const SCREEN_PANEL_SIZE: Vector2 = Vector2 { x: 300.0, y: 300.0 };
|
const SCREEN_PANEL_SIZE: Vector2 = Vector2 { x: 300.0, y: 380.0 };
|
||||||
|
|
||||||
pub struct PauseMenuScreen {}
|
pub struct PauseMenuScreen {}
|
||||||
|
|
||||||
@ -118,6 +118,15 @@ impl Screen for PauseMenuScreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render credits
|
||||||
|
draw_handle.draw_text(
|
||||||
|
"Credits:\n\t- @ewpratten\n\t- @rsninja722\n\t- @wm-c\n\t- @catarinaburghi",
|
||||||
|
(win_width / 2) - (SCREEN_PANEL_SIZE.x as i32 / 2) + 10,
|
||||||
|
(win_height / 2) - (SCREEN_PANEL_SIZE.y as i32 / 2) + 120,
|
||||||
|
20,
|
||||||
|
Color::BLACK,
|
||||||
|
);
|
||||||
|
|
||||||
// Close and quit buttons
|
// Close and quit buttons
|
||||||
let bottom_left_button_dimensions = Rectangle {
|
let bottom_left_button_dimensions = Rectangle {
|
||||||
x: (win_width as f32 / 2.0) - (SCREEN_PANEL_SIZE.x / 2.0) + 5.0,
|
x: (win_width as f32 / 2.0) - (SCREEN_PANEL_SIZE.x / 2.0) + 5.0,
|
||||||
|
@ -13,7 +13,7 @@ use lib::{utils::profiler::GameProfiler, wrappers::audio::player::AudioPlayer};
|
|||||||
use log::info;
|
use log::info;
|
||||||
use logic::{gameend::GameEndScreen, ingame::InGameScreen, loadingscreen::LoadingScreen, mainmenu::MainMenuScreen, pausemenu::PauseMenuScreen, screen::Screen};
|
use logic::{gameend::GameEndScreen, ingame::InGameScreen, loadingscreen::LoadingScreen, mainmenu::MainMenuScreen, pausemenu::PauseMenuScreen, screen::Screen};
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
use world::World;
|
use world::{World, load_world_colliders};
|
||||||
|
|
||||||
// Game Launch Configuration
|
// Game Launch Configuration
|
||||||
const DEFAULT_WINDOW_DIMENSIONS: Vector2 = Vector2 {
|
const DEFAULT_WINDOW_DIMENSIONS: Vector2 = Vector2 {
|
||||||
@ -32,7 +32,7 @@ fn main() {
|
|||||||
.size(
|
.size(
|
||||||
DEFAULT_WINDOW_DIMENSIONS.x as i32,
|
DEFAULT_WINDOW_DIMENSIONS.x as i32,
|
||||||
DEFAULT_WINDOW_DIMENSIONS.y as i32,
|
DEFAULT_WINDOW_DIMENSIONS.y as i32,
|
||||||
)
|
).msaa_4x()
|
||||||
.title(WINDOW_TITLE)
|
.title(WINDOW_TITLE)
|
||||||
.build();
|
.build();
|
||||||
raylib.set_target_fps(MAX_FPS);
|
raylib.set_target_fps(MAX_FPS);
|
||||||
@ -41,7 +41,8 @@ fn main() {
|
|||||||
raylib.set_exit_key(None);
|
raylib.set_exit_key(None);
|
||||||
|
|
||||||
// Load the world
|
// Load the world
|
||||||
let world = World::load_from_json("./assets/worlds/mainworld.json".to_string()).expect("Failed to load main world JSON");
|
let world_colliders = load_world_colliders("./assets/img/map/cave.json".to_string()).expect("Failed to load world colliders");
|
||||||
|
let world = World::load_from_json("./assets/worlds/mainworld.json".to_string(), world_colliders).expect("Failed to load main world JSON");
|
||||||
|
|
||||||
// Load the game progress
|
// Load the game progress
|
||||||
let game_progress = GameProgress::try_from_file("./assets/savestate.json".to_string());
|
let game_progress = GameProgress::try_from_file("./assets/savestate.json".to_string());
|
||||||
@ -133,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();
|
||||||
|
@ -20,3 +20,17 @@ pub const TRANSLUCENT_WHITE_64: Color = Color {
|
|||||||
b: 255,
|
b: 255,
|
||||||
a: 64,
|
a: 64,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const SKY: Color = Color {
|
||||||
|
r: 15,
|
||||||
|
g: 193,
|
||||||
|
b: 217,
|
||||||
|
a: 255
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const WATER: Color = Color {
|
||||||
|
r: 24,
|
||||||
|
g: 66,
|
||||||
|
b: 143,
|
||||||
|
a: 255
|
||||||
|
};
|
130
src/player.rs
130
src/player.rs
@ -1,6 +1,13 @@
|
|||||||
use raylib::math::Vector2;
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
@ -12,20 +19,127 @@ pub struct Player {
|
|||||||
pub breath_percent: f32,
|
pub breath_percent: f32,
|
||||||
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: Vec<ShopItems>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn new() -> Self {
|
pub fn new(spawn: &Vector2) -> Self {
|
||||||
Self {
|
Self {
|
||||||
boost_percent: 1.0,
|
boost_percent: 1.0,
|
||||||
size: Vector2 {
|
size: Vector2 { x: 11.0, y: 21.0 },
|
||||||
x: 11.0,
|
|
||||||
y: 21.0
|
|
||||||
},
|
|
||||||
breath_percent: 1.0,
|
breath_percent: 1.0,
|
||||||
|
position: spawn.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collides_with_rec(&self, rectangle: &Rectangle) -> bool {
|
||||||
|
// // Build a bounding box of the player by their corners
|
||||||
|
// let top_left_corner = self.position - (self.size / 2.0);
|
||||||
|
// let bottom_right_corner = self.position + (self.size / 2.0);
|
||||||
|
// let top_right_corner = Vector2 {
|
||||||
|
// x: bottom_right_corner.x,
|
||||||
|
// y: top_left_corner.y,
|
||||||
|
// };
|
||||||
|
// let bottom_left_corner = Vector2 {
|
||||||
|
// x: top_left_corner.x,
|
||||||
|
// y: bottom_right_corner.y,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Get the rotation
|
||||||
|
// let rotation = Vector2::zero().angle_to(self.direction);
|
||||||
|
|
||||||
|
// // Rotate the bounds
|
||||||
|
// let top_left_corner = rotate_vector(top_left_corner, rotation);
|
||||||
|
// let bottom_right_corner = rotate_vector(bottom_right_corner, rotation);
|
||||||
|
// let top_right_corner = rotate_vector(top_right_corner, rotation);
|
||||||
|
// let bottom_left_corner = rotate_vector(bottom_left_corner, rotation);
|
||||||
|
|
||||||
|
// // Check for collisions
|
||||||
|
// return rectangle.check_collision_point_rec(top_left_corner)
|
||||||
|
// || rectangle.check_collision_point_rec(bottom_right_corner)
|
||||||
|
// || 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<RaylibDrawHandle>,
|
||||||
|
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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,9 @@ pub struct GlobalResources {
|
|||||||
pub player_animation_regular: FrameAnimationWrapper,
|
pub player_animation_regular: FrameAnimationWrapper,
|
||||||
pub player_animation_boost_charge: FrameAnimationWrapper,
|
pub player_animation_boost_charge: FrameAnimationWrapper,
|
||||||
pub player_animation_boost: FrameAnimationWrapper,
|
pub player_animation_boost: FrameAnimationWrapper,
|
||||||
|
|
||||||
|
// Cave
|
||||||
|
pub cave_mid_layer: Texture2D
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalResources {
|
impl GlobalResources {
|
||||||
@ -56,6 +59,10 @@ impl GlobalResources {
|
|||||||
21,
|
21,
|
||||||
30,
|
30,
|
||||||
),
|
),
|
||||||
|
cave_mid_layer: raylib.load_texture_from_image(
|
||||||
|
&thread,
|
||||||
|
&Image::load_image("./assets/img/map/cave.png")?,
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
src/world.rs
31
src/world.rs
@ -1,6 +1,6 @@
|
|||||||
use std::{fs::File, io::BufReader};
|
use std::{fs::File, io::BufReader};
|
||||||
|
|
||||||
use raylib::math::Vector2;
|
use raylib::math::{Rectangle, Vector2};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
@ -10,16 +10,20 @@ use crate::entities::fish::FishEntity;
|
|||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub end_position: Vector2,
|
pub end_position: Vector2,
|
||||||
|
pub player_spawn: Vector2,
|
||||||
|
|
||||||
#[serde(rename = "fish")]
|
#[serde(rename = "fish")]
|
||||||
pub fish_positions: Vec<Vector2>,
|
pub fish_positions: Vec<Vector2>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub fish: Vec<FishEntity>
|
pub fish: Vec<FishEntity>,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub colliders: Vec<Rectangle>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn load_from_json(file: String) -> Result<Self, Error> {
|
pub fn load_from_json(file: String, colliders: Vec<Rectangle>) -> Result<Self, Error> {
|
||||||
// Load the file
|
// Load the file
|
||||||
let file = File::open(file)?;
|
let file = File::open(file)?;
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
@ -30,6 +34,17 @@ impl World {
|
|||||||
// Init all fish
|
// Init all fish
|
||||||
result.fish = FishEntity::new_from_positions(&result.fish_positions);
|
result.fish = FishEntity::new_from_positions(&result.fish_positions);
|
||||||
|
|
||||||
|
// Init colliders
|
||||||
|
result.colliders = Vec::new();
|
||||||
|
for collider in colliders.iter(){
|
||||||
|
result.colliders.push(Rectangle {
|
||||||
|
x: collider.x - (collider.width / 2.0),
|
||||||
|
y: collider.y - (collider.height / 2.0),
|
||||||
|
width: collider.width,
|
||||||
|
height: collider.height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,3 +60,13 @@ impl World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn load_world_colliders(file: String) -> Result<Vec<Rectangle>, Error> {
|
||||||
|
// Load the file
|
||||||
|
let file = File::open(file)?;
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
|
// Deserialize
|
||||||
|
Ok(serde_json::from_reader(reader)?)
|
||||||
|
}
|
Reference in New Issue
Block a user