diff options
author | Alexander Foremny <aforemny@posteo.de> | 2024-03-15 04:08:43 +0100 |
---|---|---|
committer | Alexander Foremny <aforemny@posteo.de> | 2024-03-15 04:08:43 +0100 |
commit | aa6901736da520d4608adfb74b8bc0a8a9e121a8 (patch) | |
tree | ac6ef01295568c60fe96b59e51382178a72ac051 | |
parent | 008477de8b99a16d929912bde73e775a075155ef (diff) |
feat: shooting range indicator
-rw-r--r-- | src/client.rs | 50 | ||||
-rw-r--r-- | src/server.rs | 90 | ||||
-rw-r--r-- | src/shared/projectile.rs | 2 |
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, |