diff --git a/src/game/camera.rs b/src/game/camera.rs index 7a9abe359e..0bcc1a29cc 100644 --- a/src/game/camera.rs +++ b/src/game/camera.rs @@ -1,8 +1,11 @@ +use macroquad::prelude::collections::storage; use macroquad::prelude::*; use macroquad::rand::gen_range; use core::noise::NoiseGenerator; +use crate::map::Map; + struct Shake { direction: (f32, f32), kind: ShakeType, @@ -49,7 +52,25 @@ impl GameCamera { } pub fn add_player_rect(&mut self, rect: Rect) { - self.player_rects.push(rect); + let map = storage::get::(); + let playable = map.get_playable_area(); + if playable.overlaps(&rect) { + self.player_rects.push(rect); + + // We don't want to try to follow the player out of the playable area, so set the + // effective player rect to the closest spot to the player that is still touching the + // playable area. + } else { + let min = Vec2::new(playable.x, playable.y); + let max = min + Vec2::new(playable.w, playable.h); + + self.player_rects.push(Rect::new( + rect.x.max(min.x).min(max.x), + rect.y.max(min.y).min(max.y), + rect.w, + rect.h, + )); + } } } diff --git a/src/game/mod.rs b/src/game/mod.rs index 2c220642f6..648577d451 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -36,7 +36,7 @@ use crate::effects::active::triggered::{fixed_update_triggered_effects, update_t use crate::items::spawn_item; use crate::map::{ debug_draw_fish_schools, fixed_update_sproingers, spawn_crab, spawn_decoration, - spawn_fish_school, spawn_sproinger, update_crabs, update_fish_schools, + spawn_fish_school, spawn_sproinger, update_crabs, update_fish_schools, update_map_kill_zone, }; use crate::network::{ fixed_update_network_client, fixed_update_network_host, update_network_client, @@ -118,6 +118,7 @@ impl Game { if matches!(mode, GameMode::Local | GameMode::NetworkHost) { updates_builder + .add_system(update_map_kill_zone) .add_system(update_player_states) .add_system(update_player_inventory) .add_system(update_player_passive_effects) diff --git a/src/map/mod.rs b/src/map/mod.rs index 687d20339c..875132fd35 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -7,11 +7,13 @@ use serde::{Deserialize, Serialize}; mod crab; mod decoration; mod fish_school; +mod player_interaction; mod sproinger; pub use crab::*; pub use decoration::*; pub use fish_school::*; +pub use player_interaction::*; pub use sproinger::*; use core::math::URect; @@ -24,6 +26,10 @@ use crate::{ Resources, }; +/// The ammount of space above the map that the player can be without being killed +const EXTRA_PLAYABLE_SKY: f32 = 200.0; +const EXTRA_PLAYABLE_WIDTH: f32 = 400.0; + pub type MapProperty = core::json::GenericParam; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -116,6 +122,19 @@ impl Map { ) } + /// Get the playable map area. + /// + /// Any player that doesn't overlap the play area will be killed. + pub fn get_playable_area(&self) -> Rect { + let size = self.get_size(); + Rect::new( + self.world_offset.x - EXTRA_PLAYABLE_WIDTH / 2.0, + self.world_offset.y - EXTRA_PLAYABLE_SKY, + size.x + EXTRA_PLAYABLE_WIDTH, + size.y + EXTRA_PLAYABLE_SKY, + ) + } + pub fn contains(&self, position: Vec2) -> bool { let map_size = self.grid_size.as_f32() * self.tile_size; let rect = Rect::new( diff --git a/src/map/player_interaction.rs b/src/map/player_interaction.rs new file mode 100644 index 0000000000..bf80bb86f2 --- /dev/null +++ b/src/map/player_interaction.rs @@ -0,0 +1,30 @@ +use core::Transform; + +use hecs::World; +use macroquad::prelude::collections::storage; + +use crate::{ + player::{Player, PlayerState}, + PhysicsBody, +}; + +use super::Map; + +pub fn update_map_kill_zone(world: &mut World) { + let map = storage::get::(); + + for (_, (player, transform, body)) in world + .query::<(&mut Player, &Transform, &PhysicsBody)>() + .iter() + { + let player: &mut Player = player; + let transform: &Transform = transform; + let body: &PhysicsBody = body; + + let player_rect = body.as_rect(transform.position); + + if !map.get_playable_area().overlaps(&player_rect) { + player.state = PlayerState::Dead; + } + } +}