From fdee6aa8cf2c51d5004d914458ff661da366e883 Mon Sep 17 00:00:00 2001 From: Alexander Foremny Date: Fri, 15 Mar 2024 14:04:17 +0100 Subject: feat: add health/ damage --- src/client.rs | 20 +++++++++++++++++- src/main.rs | 6 +++++- src/protocol.rs | 2 ++ src/server.rs | 54 +++++++++++++++++++++++++++++++++++++++++------- src/shared.rs | 4 ++++ src/shared/health.rs | 12 +++++++++++ src/shared/projectile.rs | 1 + 7 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 src/shared/health.rs (limited to 'src') diff --git a/src/client.rs b/src/client.rs index a5a7845..174d615 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,5 +1,6 @@ use crate::client::network::*; use crate::protocol::*; +use crate::shared::health::*; use crate::shared::imperative::*; use crate::shared::projectile::*; use crate::shared::*; @@ -45,8 +46,15 @@ impl Plugin for ClientPlugin { transport: self.transport.clone(), }) .add_systems(Startup, setup) - .add_systems(Update, (render_players, render_projectiles)) .add_systems(Update, (move_players, move_projectiles)) + .add_systems( + Update, + ( + render_players.after(move_players), + render_projectiles.after(move_projectiles), + render_health, + ), + ) .add_systems( FixedPreUpdate, buffer_input.in_set(InputSystemSet::BufferInputs), @@ -214,3 +222,13 @@ fn player_position( } None } + +const HEALTH_OFFSET: f32 = 4.; + +fn render_health(players: Query<(&Health, &PlayerPosition)>, mut gizmos: Gizmos) { + for (health, position) in players.iter() { + let start = position.0 + Vec2::new(-PLAYER_RADIUS, PLAYER_RADIUS + HEALTH_OFFSET); + let end = position.0 + Vec2::new(PLAYER_RADIUS, PLAYER_RADIUS + HEALTH_OFFSET); + gizmos.line_2d(start, start.lerp(end, health.0 / MAX_HEALTH), Color::RED); + } +} diff --git a/src/main.rs b/src/main.rs index f213197..b615ddc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,11 @@ fn main() { server::main(TransportConfig::UdpSocket(server_addr)); } else if let Some(server_addr) = cli.connect_to { let client_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), SERVER_PORT + client_id); - client::main(Some(server_addr), client_id as u64, TransportConfig::UdpSocket(client_addr)); + client::main( + Some(server_addr), + client_id as u64, + TransportConfig::UdpSocket(client_addr), + ); } else { let (from_server_send, from_server_recv) = crossbeam_channel::unbounded(); let (to_server_send, to_server_recv) = crossbeam_channel::unbounded(); diff --git a/src/protocol.rs b/src/protocol.rs index 7672489..3d4567d 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,4 +1,5 @@ use crate::shared::cooldown::*; +use crate::shared::health::*; use crate::shared::imperative::*; use crate::shared::projectile::*; use crate::shared::*; @@ -31,6 +32,7 @@ pub enum Components { Projectile(Projectile), ProjectilePosition(ProjectilePosition), Cooldown(Cooldown), + Health(Health), } #[derive(Channel)] diff --git a/src/server.rs b/src/server.rs index c70b39e..fab529f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,7 @@ use crate::protocol::*; use crate::server::network::*; use crate::shared::cooldown::*; +use crate::shared::health::*; use crate::shared::imperative::*; use crate::shared::projectile::*; use crate::shared::*; @@ -26,14 +27,28 @@ struct ServerPlugin { pub transport: TransportConfig, } +const HEALTH_REGEN_RATE: f32 = 1.; + +#[derive(Resource)] +struct HealthRegenTimer(pub Timer); + +impl Default for HealthRegenTimer { + fn default() -> Self { + HealthRegenTimer(Timer::from_seconds(HEALTH_REGEN_RATE, TimerMode::Repeating)) + } +} + impl Plugin for ServerPlugin { fn build(&self, app: &mut App) { app.insert_resource(EntityMap::default()) + .insert_resource(HealthRegenTimer::default()) .add_plugins(NetworkPlugin { transport: self.transport.clone(), }) .add_systems(Startup, setup) .add_systems(Update, (connects, disconnects)) + .add_systems(Update, timers_ticket) + .add_systems(Update, health_regen.after(timers_ticket)) .add_systems( Update, ( @@ -186,6 +201,7 @@ fn imperative_attack( projectile: Projectile { target_player, source_player: *id, + damage: 4., }, position: ProjectilePosition(position.0), replicate: Replicate::default(), @@ -236,19 +252,29 @@ fn projectile_move( fn projectile_despawn( entity_map: Res, mut commands: Commands, - projectile_positions: Query<&ProjectilePosition>, + mut healths: Query<&mut Health>, player_positions: Query<&PlayerPosition>, + projectile_positions: Query<&ProjectilePosition>, projectiles: Query<(Entity, &mut Projectile)>, ) { for (entity, projectile) in projectiles.iter() { - if let Some(target_entity) = entity_map.0.get(&projectile.target_player.0) { - if let Ok(position) = projectile_positions.get(entity) { - if let Ok(target_position) = player_positions.get(*target_entity) { - if position.0.distance(target_position.0) <= f32::EPSILON { - commands.entity(entity).despawn(); - } - } + let Some(target_entity) = entity_map.0.get(&projectile.target_player.0) else { + commands.entity(entity).despawn(); + return; + }; + let Ok(position) = projectile_positions.get(entity) else { + commands.entity(entity).despawn(); + return; + }; + let Ok(target_position) = player_positions.get(*target_entity) else { + commands.entity(entity).despawn(); + return; + }; + if position.0.distance(target_position.0) <= f32::EPSILON { + if let Ok(mut health) = healths.get_mut(*target_entity) { + health.0 = (health.0 - projectile.damage).max(0.); } + commands.entity(entity).despawn(); } } } @@ -263,3 +289,15 @@ fn cooldown_decrement(mut cooldowns: Query<&mut Cooldown>, time: Res