From ee2bc9dca88e52b92a49a9ff21db8832b3b66255 Mon Sep 17 00:00:00 2001
From: Alexander Foremny <aforemny@posteo.de>
Date: Tue, 19 Mar 2024 03:00:42 +0100
Subject: feat: dash ability

---
 src/shared/ability.rs | 137 ++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 123 insertions(+), 14 deletions(-)

(limited to 'src/shared/ability.rs')

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,
-- 
cgit v1.2.3