From 601474aa84cfc071c724051ac121e3c9cc796577 Mon Sep 17 00:00:00 2001 From: Alexander Foremny Date: Sun, 17 Mar 2024 09:26:25 +0100 Subject: feat: skillshots --- src/client.rs | 89 ++++++++++++++++-------- src/protocol.rs | 1 - src/server.rs | 174 ++++++++++++++++++++++++++++++++++++----------- src/shared.rs | 1 + src/shared/imperative.rs | 3 +- src/shared/projectile.rs | 29 ++++++-- 6 files changed, 223 insertions(+), 74 deletions(-) (limited to 'src') diff --git a/src/client.rs b/src/client.rs index b2db950..7f70549 100644 --- a/src/client.rs +++ b/src/client.rs @@ -15,7 +15,6 @@ use std::net::SocketAddr; mod network; -const PLAYER_RADIUS: f32 = 10.; const PLAYER_HOVER_INDICATOR_RADIUS: f32 = 13.; const PLAYER_HOVER_RADIUS: f32 = 20.; @@ -111,23 +110,35 @@ fn render_projectiles( mut commands: Commands, mut materials: ResMut>, mut meshes: ResMut>, - projectiles: Query<(Entity, &ProjectilePosition), Added>, + projectiles: Query<(Entity, &Projectile), Added>, ) { - for (entity, position) in projectiles.iter() { + for (entity, projectile) in projectiles.iter() { + let Some(position) = (match projectile.type_ { + ProjectileType::Free(FreeProjectile { position, .. }) => Some(position), + ProjectileType::Instant(InstantProjectile { .. }) => None, + ProjectileType::Targeted(TargetedProjectile { position, .. }) => Some(position), + }) else { + continue; + }; commands.entity(entity).insert(MaterialMesh2dBundle { mesh: Mesh2dHandle(meshes.add(Circle { radius: 2. })), material: materials.add(Color::RED), - transform: Transform::from_xyz(position.0.x, position.0.y, 1.), + transform: Transform::from_xyz(position.x, position.y, 1.), ..Default::default() }); } } -fn move_projectiles( - mut projectiles: Query<(&mut Transform, &ProjectilePosition), Changed>, -) { - for (mut transform, projectile_position) in projectiles.iter_mut() { - transform.translation = Vec3::new(projectile_position.0.x, projectile_position.0.y, 0.); +fn move_projectiles(mut projectiles: Query<(&mut Transform, &Projectile), Changed>) { + for (mut transform, projectile) in projectiles.iter_mut() { + let Some(position) = (match projectile.type_ { + ProjectileType::Free(FreeProjectile { position, .. }) => Some(position), + ProjectileType::Instant(InstantProjectile { .. }) => None, + ProjectileType::Targeted(TargetedProjectile { position, .. }) => Some(position), + }) else { + continue; + }; + transform.translation = Vec3::new(position.x, position.y, 0.); } } @@ -138,46 +149,66 @@ fn move_players(mut players: Query<(&mut Transform, &PlayerPosition), Changed, + players: Query<(&PlayerId, &PlayerPosition)>, + mut attack: ResMut, cameras: Query<(&Camera, &GlobalTransform)>, client_id: Res, mouse_input: Res>, mut client: ClientMut, windows: Query<&Window>, - attack: Res, ) { if mouse_input.just_pressed(MouseButton::Left) { - if let Some((target_player, _)) = - hovered_other_player(&cameras, &client_id, &attackables, &windows) - { - client.add_input(Inputs::Imperative(Imperative::Attack( - attack.0.unwrap_or(AttackKey::A), - target_player, - ))); - } else { - if let Some(world_position) = cursor_world_position(&windows, &cameras) { - client.add_input(Inputs::Imperative(Imperative::WalkTo(world_position))); + match attack.0 { + Some(AttackKey::Q) => { + let Some(world_position) = cursor_world_position(&windows, &cameras) else { + return; + }; + let Some(position) = player_position(&client_id, &players) else { + return; + }; + let Some(direction) = (world_position - position.0).try_normalize() else { + return; + }; + client.add_input(Inputs::Imperative(Imperative::AttackDirection( + AttackKey::Q, + direction, + ))); + attack.0 = None; + } + _ => { + if let Some((target_player, _)) = + hovered_other_player(&cameras, &client_id, &players, &windows) + { + client.add_input(Inputs::Imperative(Imperative::AttackTarget( + attack.0.unwrap_or(AttackKey::A), + target_player, + ))); + } else { + if let Some(world_position) = cursor_world_position(&windows, &cameras) { + client.add_input(Inputs::Imperative(Imperative::WalkTo(world_position))); + } + } } } } } fn choose_attack(keyboard_input: Res>, mut attack_key: ResMut) { - if keyboard_input.pressed(KeyCode::KeyA) { + if keyboard_input.just_pressed(KeyCode::KeyA) { attack_key.0 = Some(AttackKey::A); - } else if keyboard_input.pressed(KeyCode::KeyQ) { + } else if keyboard_input.just_pressed(KeyCode::KeyQ) { attack_key.0 = Some(AttackKey::Q); - } else if keyboard_input.pressed(KeyCode::KeyW) { + } else if keyboard_input.just_pressed(KeyCode::KeyW) { attack_key.0 = Some(AttackKey::W); - } else if keyboard_input.pressed(KeyCode::KeyE) { + } else if keyboard_input.just_pressed(KeyCode::KeyE) { attack_key.0 = Some(AttackKey::E); - } else if keyboard_input.pressed(KeyCode::KeyR) { + } else if keyboard_input.just_pressed(KeyCode::KeyR) { attack_key.0 = Some(AttackKey::R); - } else if keyboard_input.pressed(KeyCode::KeyD) { + } else if keyboard_input.just_pressed(KeyCode::KeyD) { attack_key.0 = Some(AttackKey::D); - } else if keyboard_input.pressed(KeyCode::KeyF) { + } else if keyboard_input.just_pressed(KeyCode::KeyF) { attack_key.0 = Some(AttackKey::F); - } else if keyboard_input.pressed(KeyCode::Escape) { + } else if keyboard_input.just_pressed(KeyCode::Escape) { attack_key.0 = None; } } diff --git a/src/protocol.rs b/src/protocol.rs index 7ec3dcb..70f4f64 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -31,7 +31,6 @@ pub enum Components { PlayerPosition(PlayerPosition), PlayerColor(PlayerColor), Projectile(Projectile), - ProjectilePosition(ProjectilePosition), Cooldown(Cooldown), Health(Health), Champion(Champion), diff --git a/src/server.rs b/src/server.rs index e3d5a72..b2f22b3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -189,6 +189,10 @@ fn move_to_target(position: Vec2, target_position: Vec2, max_distance: f32) -> ( } } +fn move_along_direction(position: Vec2, direction: Vec2, distance: f32) -> Vec2 { + position + distance * direction +} + fn imperative_attack_approach( entity_map: Res, mut players: Query<(&PlayerId, &mut Imperative)>, @@ -198,7 +202,7 @@ fn imperative_attack_approach( ) { for (id, mut imperative) in players.iter_mut() { match *imperative { - Imperative::Attack(AttackKey::A, target_player) => { + Imperative::AttackTarget(_, target_player) => { let Some(entity) = entity_map.0.get(&id.0) else { *imperative = Imperative::Idle; return; @@ -242,7 +246,7 @@ fn imperative_attack_attack( ) { for (id, mut imperative) in players.iter_mut() { match *imperative { - Imperative::Attack(AttackKey::A, target_player) => { + Imperative::AttackTarget(_, target_player) => { let Some(entity) = entity_map.0.get(&id.0) else { *imperative = Imperative::Idle; return; @@ -269,24 +273,54 @@ fn imperative_attack_attack( }; if cooldown.a_cooldown.is_zero() { cooldown.a_cooldown = Duration::from_secs_f32(1.5); - let instant = *champion == Champion::Meele; + let projectile_type = if *champion == Champion::Meele { + ProjectileType::Instant(InstantProjectile { target_player }) + } else { + ProjectileType::Targeted(TargetedProjectile { + target_player, + position: position.0, + }) + }; commands.spawn(ProjectileBundle { projectile: Projectile { - target_player, source_player: *id, damage: 4., - instant, + type_: projectile_type, }, - position: ProjectilePosition(if instant { - target_position.0 - } else { - position.0 - }), replicate: Replicate::default(), }); } } } + Imperative::AttackDirection(_, direction) => { + let Some(entity) = entity_map.0.get(&id.0) else { + *imperative = Imperative::Idle; + return; + }; + let Ok(position) = positions.get_mut(*entity) else { + *imperative = Imperative::Idle; + return; + }; + let Ok(mut cooldown) = cooldowns.get_mut(*entity) else { + *imperative = Imperative::Idle; + return; + }; + if cooldown.a_cooldown.is_zero() { + cooldown.a_cooldown = Duration::from_secs_f32(1.5); + commands.spawn(ProjectileBundle { + projectile: Projectile { + source_player: *id, + damage: 4., + type_: ProjectileType::Free(FreeProjectile { + position: position.0, + direction, + starting_position: position.0, + }), + }, + replicate: Replicate::default(), + }); + } + } _ => {} } } @@ -296,24 +330,40 @@ const PROJECTILE_SPEED: f32 = 150.; fn projectile_move( entity_map: Res, - mut projectile_positions: Query<&mut ProjectilePosition>, player_positions: Query<&PlayerPosition>, - projectiles: Query<(Entity, &mut Projectile)>, + mut projectiles: Query<&mut Projectile>, time: Res