aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-15 14:04:17 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-15 14:25:01 +0100
commitfdee6aa8cf2c51d5004d914458ff661da366e883 (patch)
tree0c022856e64e72a00305e28a684e139696c297ab /src
parent1e162629da53373f0e2502014d42c7636412e67e (diff)
feat: add health/ damage
Diffstat (limited to 'src')
-rw-r--r--src/client.rs20
-rw-r--r--src/main.rs6
-rw-r--r--src/protocol.rs2
-rw-r--r--src/server.rs54
-rw-r--r--src/shared.rs4
-rw-r--r--src/shared/health.rs12
-rw-r--r--src/shared/projectile.rs1
7 files changed, 89 insertions, 10 deletions
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,9 +46,16 @@ 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<EntityMap>,
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<Time>) {
cooldown.f_cooldown = cooldown.f_cooldown.saturating_sub(time.delta());
}
}
+
+fn timers_ticket(mut health_regen_timer: ResMut<HealthRegenTimer>, time: Res<Time>) {
+ health_regen_timer.0.tick(time.delta());
+}
+
+fn health_regen(health_regen_timer: Res<HealthRegenTimer>, mut healths: Query<&mut Health>) {
+ if health_regen_timer.0.just_finished() {
+ for mut health in healths.iter_mut() {
+ health.0 = (health.0 + 1.).min(MAX_HEALTH);
+ }
+ }
+}
diff --git a/src/shared.rs b/src/shared.rs
index b3a7cc4..92b6a5b 100644
--- a/src/shared.rs
+++ b/src/shared.rs
@@ -1,5 +1,6 @@
use crate::protocol::Replicate;
use crate::shared::cooldown::*;
+use crate::shared::health::*;
use crate::shared::imperative::*;
use bevy::prelude::*;
use lightyear::prelude::*;
@@ -8,6 +9,7 @@ use serde::Serialize;
use std::default::Default;
pub mod cooldown;
+pub mod health;
pub mod imperative;
pub mod projectile;
@@ -25,6 +27,7 @@ pub struct PlayerBundle {
replicate: Replicate,
imperative: Imperative,
cooldown: Cooldown,
+ health: Health,
}
#[derive(Component, Message, Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
@@ -45,6 +48,7 @@ impl PlayerBundle {
replicate: Replicate::default(),
imperative: Imperative::Idle,
cooldown: Cooldown::default(),
+ health: Health::default(),
}
}
}
diff --git a/src/shared/health.rs b/src/shared/health.rs
new file mode 100644
index 0000000..f4a288a
--- /dev/null
+++ b/src/shared/health.rs
@@ -0,0 +1,12 @@
+use crate::shared::*;
+
+#[derive(Component, Message, Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
+pub struct Health(pub f32);
+
+pub const MAX_HEALTH: f32 = 100.;
+
+impl Default for Health {
+ fn default() -> Self {
+ Health(MAX_HEALTH)
+ }
+}
diff --git a/src/shared/projectile.rs b/src/shared/projectile.rs
index 8dc9da7..c42173a 100644
--- a/src/shared/projectile.rs
+++ b/src/shared/projectile.rs
@@ -13,6 +13,7 @@ pub struct ProjectileBundle {
pub struct Projectile {
pub target_player: PlayerId,
pub source_player: PlayerId,
+ pub damage: f32,
}
#[derive(Component, Message, Serialize, Deserialize, Clone, Debug, PartialEq)]