aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-15 04:08:43 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-15 04:08:43 +0100
commitaa6901736da520d4608adfb74b8bc0a8a9e121a8 (patch)
treeac6ef01295568c60fe96b59e51382178a72ac051
parent008477de8b99a16d929912bde73e775a075155ef (diff)
feat: shooting range indicator
-rw-r--r--src/client.rs50
-rw-r--r--src/server.rs90
-rw-r--r--src/shared/projectile.rs2
3 files changed, 100 insertions, 42 deletions
diff --git a/src/client.rs b/src/client.rs
index 96baeb8..8793c4a 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -32,15 +32,13 @@ impl Plugin for ClientPlugin {
transport: self.transport.clone(),
})
.add_systems(Startup, setup)
- .add_systems(Update, render_players)
- .add_systems(Update, render_projectiles)
- .add_systems(Update, move_players)
- .add_systems(Update, move_projectiles)
+ .add_systems(Update, (render_players, render_projectiles))
+ .add_systems(Update, (move_players, move_projectiles))
.add_systems(
FixedPreUpdate,
buffer_input.in_set(InputSystemSet::BufferInputs),
)
- .add_systems(Last, gizmos_hover_indicator);
+ .add_systems(Last, (gizmos_hover_indicator, gizmos_attack_indicator));
}
}
@@ -105,7 +103,7 @@ fn buffer_input(
windows: Query<&Window>,
) {
if mouse_input.just_pressed(MouseButton::Left) {
- if let Some((target_player, _)) = hovered_player(&windows, &cameras, &attackables) {
+ if let Some((target_player, _)) = hovered_other_player(&windows, &cameras, &attackables) {
client.add_input(Inputs::Imperative(Imperative::Attack(target_player)));
} else {
if let Some(world_position) = cursor_world_position(&windows, &cameras) {
@@ -121,7 +119,7 @@ fn gizmos_hover_indicator(
cameras: Query<(&Camera, &GlobalTransform)>,
windows: Query<&Window>,
) {
- let Some((_, position)) = hovered_player(&windows, &cameras, &hoverables) else {
+ let Some((_, position)) = hovered_other_player(&windows, &cameras, &hoverables) else {
return;
};
gizmos.circle_2d(position.0, PLAYER_HOVER_INDICATOR_RADIUS, Color::GREEN);
@@ -139,6 +137,20 @@ fn cursor_world_position(
camera.viewport_to_world_2d(camera_transform, cursor_position)
}
+fn hovered_other_player(
+ windows: &Query<&Window>,
+ cameras: &Query<(&Camera, &GlobalTransform)>,
+ hoverables: &Query<(&PlayerId, &PlayerPosition)>,
+) -> Option<(PlayerId, PlayerPosition)> {
+ let Some((id, position)) = hovered_player(windows, cameras, hoverables) else {
+ return None;
+ };
+ if id.0 == 0 {
+ return None;
+ }
+ Some((id, position))
+}
+
fn hovered_player(
windows: &Query<&Window>,
cameras: &Query<(&Camera, &GlobalTransform)>,
@@ -155,3 +167,27 @@ fn hovered_player(
}
hovered_player
}
+
+fn gizmos_attack_indicator(
+ cameras: Query<(&Camera, &GlobalTransform)>,
+ hoverables: Query<(&PlayerId, &PlayerPosition)>,
+ mut gizmos: Gizmos,
+ players: Query<(&PlayerId, &PlayerPosition)>,
+ windows: Query<&Window>,
+) {
+ let Some(position) = player_position(&players) else {
+ return;
+ };
+ if hovered_other_player(&windows, &cameras, &hoverables).is_some() {
+ gizmos.circle_2d(position.0, ATTACK_RANGE, Color::YELLOW);
+ }
+}
+
+fn player_position(players: &Query<(&PlayerId, &PlayerPosition)>) -> Option<PlayerPosition> {
+ for (id, position) in players.iter() {
+ if id.0 == 0 {
+ return Some(*position);
+ }
+ }
+ None
+}
diff --git a/src/server.rs b/src/server.rs
index 52daf1a..c70b39e 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -125,16 +125,14 @@ fn imperative_walk_to(
match *imperative {
Imperative::WalkTo(target_position) => {
if let Ok(mut position) = positions.get_mut(entity) {
- let distance = (target_position - position.0).length();
- let direction = (target_position - position.0).normalize_or_zero();
- let new_position = position.0
- + f32::min(MOVEMENT_SPEED * time.delta().as_secs_f32(), distance)
- * direction;
- if position.0.distance(new_position) < f32::EPSILON {
- position.0 = target_position;
+ let (new_position, target_reached) = move_to_target(
+ position.0,
+ target_position,
+ MOVEMENT_SPEED * time.delta().as_secs_f32(),
+ );
+ position.0 = new_position;
+ if target_reached {
*imperative = Imperative::Idle;
- } else {
- position.0 = new_position;
}
} else {
*imperative = Imperative::Idle;
@@ -145,29 +143,57 @@ fn imperative_walk_to(
}
}
+fn move_to_target(position: Vec2, target_position: Vec2, max_distance: f32) -> (Vec2, bool) {
+ let distance = (target_position - position).length();
+ let direction = (target_position - position).normalize_or_zero();
+ let new_position = position + f32::min(max_distance, distance) * direction;
+ if position.distance(new_position) <= f32::EPSILON {
+ (target_position, true)
+ } else {
+ (new_position, false)
+ }
+}
+
fn imperative_attack(
entity_map: Res<EntityMap>,
mut commands: Commands,
mut cooldowns: Query<&mut Cooldown>,
mut players: Query<(&PlayerId, &mut Imperative)>,
- positions: Query<&PlayerPosition>,
+ mut positions: Query<&mut PlayerPosition>,
+ time: Res<Time>,
) {
for (id, mut imperative) in players.iter_mut() {
match *imperative {
Imperative::Attack(target_player) => {
if let Some(entity) = entity_map.0.get(&id.0) {
- if let Ok(position) = positions.get(*entity) {
- if let Ok(mut cooldown) = cooldowns.get_mut(*entity) {
- if cooldown.a_cooldown.is_zero() {
- cooldown.a_cooldown = Duration::from_secs_f32(1.5);
- commands.spawn(ProjectileBundle {
- projectile: Projectile {
- target_player,
- source_player: *id,
- },
- position: ProjectilePosition(position.0),
- replicate: Replicate::default(),
- });
+ if let Some(target_entity) = entity_map.0.get(&target_player.0) {
+ if let Ok([mut position, target_position]) =
+ positions.get_many_mut([*entity, *target_entity])
+ {
+ let distance = target_position.0.distance(position.0);
+ if distance > ATTACK_RANGE {
+ let (new_position, _) = move_to_target(
+ position.0,
+ target_position.0,
+ MOVEMENT_SPEED * time.delta().as_secs_f32(),
+ );
+ position.0 = new_position;
+ } else {
+ if let Ok(mut cooldown) = cooldowns.get_mut(*entity) {
+ if cooldown.a_cooldown.is_zero() {
+ cooldown.a_cooldown = Duration::from_secs_f32(1.5);
+ commands.spawn(ProjectileBundle {
+ projectile: Projectile {
+ target_player,
+ source_player: *id,
+ },
+ position: ProjectilePosition(position.0),
+ replicate: Replicate::default(),
+ });
+ }
+ } else {
+ }
+ *imperative = Imperative::Idle;
}
} else {
*imperative = Imperative::Idle;
@@ -175,8 +201,6 @@ fn imperative_attack(
} else {
*imperative = Imperative::Idle;
}
- } else {
- *imperative = Imperative::Idle;
}
}
_ => {}
@@ -197,16 +221,12 @@ fn projectile_move(
if let Some(target_entity) = entity_map.0.get(&projectile.target_player.0) {
if let Ok(mut position) = projectile_positions.get_mut(entity) {
if let Ok(target_position) = player_positions.get(*target_entity) {
- let distance = (target_position.0 - position.0).length();
- let direction = (target_position.0 - position.0).normalize_or_zero();
- let new_position = position.0
- + f32::min(PROJECTILE_SPEED * time.delta().as_secs_f32(), distance)
- * direction;
- if position.0.distance(new_position) < f32::EPSILON {
- position.0 = target_position.0;
- } else {
- position.0 = new_position;
- }
+ let (new_position, _) = move_to_target(
+ position.0,
+ target_position.0,
+ PROJECTILE_SPEED * time.delta().as_secs_f32(),
+ );
+ position.0 = new_position;
}
}
}
@@ -224,7 +244,7 @@ fn projectile_despawn(
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 {
+ if position.0.distance(target_position.0) <= f32::EPSILON {
commands.entity(entity).despawn();
}
}
diff --git a/src/shared/projectile.rs b/src/shared/projectile.rs
index 59dec30..8dc9da7 100644
--- a/src/shared/projectile.rs
+++ b/src/shared/projectile.rs
@@ -1,5 +1,7 @@
use crate::shared::*;
+pub const ATTACK_RANGE: f32 = 60.;
+
#[derive(Bundle)]
pub struct ProjectileBundle {
pub projectile: Projectile,