aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-19 03:00:42 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-19 04:51:10 +0100
commitee2bc9dca88e52b92a49a9ff21db8832b3b66255 (patch)
tree43c3d85107eb0ac1286c50094e6d78ee913a683f
parent66b4afc529e6d1c4b8f624bbfddc0195ce8d17d5 (diff)
feat: dash ability
-rw-r--r--src/client.rs16
-rw-r--r--src/server.rs28
-rw-r--r--src/shared/ability.rs137
-rw-r--r--src/shared/champion.rs2
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),
},