diff options
author | Alexander Foremny <aforemny@posteo.de> | 2024-03-19 03:00:42 +0100 |
---|---|---|
committer | Alexander Foremny <aforemny@posteo.de> | 2024-03-19 04:51:10 +0100 |
commit | ee2bc9dca88e52b92a49a9ff21db8832b3b66255 (patch) | |
tree | 43c3d85107eb0ac1286c50094e6d78ee913a683f /src | |
parent | 66b4afc529e6d1c4b8f624bbfddc0195ce8d17d5 (diff) |
feat: dash ability
Diffstat (limited to 'src')
-rw-r--r-- | src/client.rs | 16 | ||||
-rw-r--r-- | src/server.rs | 28 | ||||
-rw-r--r-- | src/shared/ability.rs | 137 | ||||
-rw-r--r-- | src/shared/champion.rs | 2 |
4 files changed, 153 insertions, 30 deletions
diff --git a/src/client.rs b/src/client.rs index bd2c448..22e3960 100644 --- a/src/client.rs +++ b/src/client.rs @@ -429,6 +429,22 @@ fn gizmos_attack_indicator( }; match champion.to_ability(ability_slot) { Ability::Activated(_) => {} + Ability::Directional(DirectionalAbility::Dash) => { + let Some(world_position) = cursor_world_position(&windows, &cameras) else { + return; + }; + let Some(direction) = (world_position - position.0).try_normalize() else { + return; + }; + let dash_end = dash_collision( + PlayerId(client_id.0), + position.0, + direction, + 150., + &player_positions, + ); + gizmos.arrow_2d(position.0, dash_end, Color::YELLOW); + } Ability::Directional(_) => { let Some(world_position) = cursor_world_position(&windows, &cameras) else { return; diff --git a/src/server.rs b/src/server.rs index 0f34a12..8507244 100644 --- a/src/server.rs +++ b/src/server.rs @@ -301,21 +301,19 @@ fn imperative_attack_attack( *imperative = Imperative::Idle; continue; }; - let Ok(position) = positions.get_mut(*entity) else { - *imperative = Imperative::Idle; - continue; - }; - let Ok(mut cooldown) = cooldowns.get_mut(*entity) else { - *imperative = Imperative::Idle; - continue; - }; - let base_cooldown = BaseCooldown::from_champion(*champion); - if cooldown.0[ability_slot].is_zero() { - cooldown.0[ability_slot] = base_cooldown.0[ability_slot]; - commands.spawn(ProjectileBundle::new( - ability.to_projectile(*id, position.0, direction), - )); - *imperative = Imperative::Idle; + match ability.activate() { + DirectionalAbilityActivation(run) => { + let Ok(mut cooldown) = cooldowns.get_mut(*entity) else { + *imperative = Imperative::Idle; + continue; + }; + let base_cooldown = BaseCooldown::from_champion(*champion); + if cooldown.0[ability_slot].is_zero() { + cooldown.0[ability_slot] = base_cooldown.0[ability_slot]; + run(&mut commands, *id, direction); + } + *imperative = Imperative::Idle; + } } } _ => {} diff --git a/src/shared/ability.rs b/src/shared/ability.rs index dd843e0..e8b2a26 100644 --- a/src/shared/ability.rs +++ b/src/shared/ability.rs @@ -1,6 +1,7 @@ use crate::shared::player::*; use crate::shared::projectile::*; use crate::shared::*; +use bevy::ecs::system::*; use bevy::utils::Duration; use std::ops::*; @@ -49,30 +50,138 @@ pub enum ActivatedAbility { #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub enum DirectionalAbility { + Dash, Spear, } +pub struct DirectionalAbilityActivation( + pub fn(commands: &mut Commands, source_player: PlayerId, direction: Vec2) -> (), +); + impl DirectionalAbility { - pub fn to_projectile( - self, - source_player: PlayerId, - position: Vec2, - direction: Vec2, - ) -> Projectile { + pub fn activate(self) -> DirectionalAbilityActivation { match self { - DirectionalAbility::Spear => Projectile { - type_: ProjectileType::Free(FreeProjectile { - position, - direction, - starting_position: position, - }), - source_player, - damage: 15., + DirectionalAbility::Dash => DirectionalAbilityActivation(dash_activation), + DirectionalAbility::Spear => DirectionalAbilityActivation(spear_activation), + } + } +} + +fn dash_activation(commands: &mut Commands, source_player: PlayerId, direction: Vec2) { + commands.add(move |world: &mut World| { + world.run_system_once( + move |players: Query<(Entity, &PlayerId)>, + mut set: ParamSet<( + Query<&mut PlayerPosition>, + Query<(&PlayerId, &PlayerPosition)>, + )>| { + let Some(source_entity) = ({ + let mut source_entity = None; + for (entity, player_id) in players.iter() { + if *player_id != source_player { + continue; + } + source_entity = Some(entity); + break; + } + source_entity + }) else { + return; + }; + + let Some(source_position) = ({ + let positions = set.p0(); + if let Ok(position) = positions.get(source_entity) { + Some(*position) + } else { + None + } + }) else { + return; + }; + + let dash_end = { + let dash_targets = set.p1(); + dash_collision( + source_player, + source_position.0, + direction, + 150., + &dash_targets, + ) + }; + + let mut positions = set.p0(); + if let Ok(mut position) = positions.get_mut(source_entity) { + position.0 = dash_end; + } }, + ) + }); +} + +pub fn dash_collision( + source_id: PlayerId, + dash_start: Vec2, + dash_direction: Vec2, + dash_max_distance: f32, + player_positions: &Query<(&PlayerId, &PlayerPosition)>, +) -> Vec2 { + let mut dash_collision = dash_max_distance * dash_direction; + let mut collision = false; + for (player_id, position) in player_positions.iter() { + if *player_id == source_id { + continue; + } + + let player_position = position.0 - dash_start; + let player_projection = player_position.project_onto(dash_collision); + let player_rejection = player_position - player_projection; + let scalar_factor = player_projection.dot(dash_collision).signum() + * player_projection.length() + / dash_collision.length(); + + if scalar_factor < 0. || scalar_factor > 1.0 { + continue; } + + if player_rejection.length() < 2. * PLAYER_RADIUS { + collision = true; + dash_collision = player_projection; + } + } + + if collision { + dash_start + + (dash_collision.length() - 2. * PLAYER_RADIUS) * dash_collision.normalize_or_zero() + } else { + dash_start + dash_max_distance * dash_direction } } +fn spear_activation(commands: &mut Commands, source_player: PlayerId, direction: Vec2) { + commands.add(move |world: &mut World| { + world.run_system_once( + move |mut commands: Commands, players: Query<(&PlayerId, &PlayerPosition)>| { + for (id, position) in players.iter() { + if *id != source_player { + continue; + } + commands.spawn(ProjectileBundle::new(Projectile { + type_: ProjectileType::Free(FreeProjectile { + position: position.0, + direction, + starting_position: position.0, + }), + source_player, + damage: 15., + })); + } + }, + ) + }); +} + #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] pub enum AbilitySlot { A, diff --git a/src/shared/champion.rs b/src/shared/champion.rs index c32d3d5..8dadd9b 100644 --- a/src/shared/champion.rs +++ b/src/shared/champion.rs @@ -30,7 +30,7 @@ impl Champion { pub fn to_ability(self, ability_slot: AbilitySlot) -> Ability { match self { Champion::Meele => match ability_slot { - AbilitySlot::Q => Ability::Directional(DirectionalAbility::Spear), + AbilitySlot::Q => Ability::Directional(DirectionalAbility::Dash), AbilitySlot::G => Ability::Activated(ActivatedAbility::Speed), _ => Ability::Targeted(TargetedAbility::MeeleAttack), }, |