From 885d0bf2c02fc69fc890765d65067c01741ce47b Mon Sep 17 00:00:00 2001
From: Alexander Foremny <aforemny@posteo.de>
Date: Sun, 17 Mar 2024 13:37:58 +0100
Subject: chore: refactor abilities

---
 src/client.rs            | 51 +++++++++++++++++++++++--------------
 src/server.rs            | 28 +++------------------
 src/shared.rs            |  1 +
 src/shared/ability.rs    | 65 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/champion.rs   | 16 ++++++++++++
 src/shared/imperative.rs |  5 ++--
 6 files changed, 122 insertions(+), 44 deletions(-)
 create mode 100644 src/shared/ability.rs

diff --git a/src/client.rs b/src/client.rs
index 7f70549..886afd0 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,5 +1,6 @@
 use crate::client::network::*;
 use crate::protocol::*;
+use crate::shared::ability::*;
 use crate::shared::champion::*;
 use crate::shared::health::*;
 use crate::shared::imperative::*;
@@ -156,31 +157,45 @@ fn buffer_input(
     mouse_input: Res<ButtonInput<MouseButton>>,
     mut client: ClientMut,
     windows: Query<&Window>,
+    champion: Res<MyChampion>,
 ) {
     if mouse_input.just_pressed(MouseButton::Left) {
         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;
-            }
-            _ => {
+            Some(attack_key) => match champion.0.to_ability(attack_key) {
+                Ability::Directional(ability) => {
+                    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(
+                        ability, direction,
+                    )));
+                    attack.0 = None;
+                }
+                Ability::Targeted(ability) => {
+                    let Some((target_player, _)) =
+                        hovered_other_player(&cameras, &client_id, &players, &windows)
+                    else {
+                        return;
+                    };
+                    client.add_input(Inputs::Imperative(Imperative::AttackTarget(
+                        ability,
+                        target_player,
+                    )));
+                    attack.0 = None;
+                }
+            },
+            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),
+                        TargetedAbility::MeeleAttack,
                         target_player,
                     )));
                 } else {
diff --git a/src/server.rs b/src/server.rs
index b2f22b3..f6cbac3 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -246,7 +246,7 @@ fn imperative_attack_attack(
 ) {
     for (id, mut imperative) in players.iter_mut() {
         match *imperative {
-            Imperative::AttackTarget(_, target_player) => {
+            Imperative::AttackTarget(ability, target_player) => {
                 let Some(entity) = entity_map.0.get(&id.0) else {
                     *imperative = Imperative::Idle;
                     return;
@@ -273,26 +273,14 @@ fn imperative_attack_attack(
                     };
                     if cooldown.a_cooldown.is_zero() {
                         cooldown.a_cooldown = Duration::from_secs_f32(1.5);
-                        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 {
-                                source_player: *id,
-                                damage: 4.,
-                                type_: projectile_type,
-                            },
+                            projectile: ability.to_projectile(*id, position.0, target_player),
                             replicate: Replicate::default(),
                         });
                     }
                 }
             }
-            Imperative::AttackDirection(_, direction) => {
+            Imperative::AttackDirection(ability, direction) => {
                 let Some(entity) = entity_map.0.get(&id.0) else {
                     *imperative = Imperative::Idle;
                     return;
@@ -308,15 +296,7 @@ fn imperative_attack_attack(
                 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,
-                            }),
-                        },
+                        projectile: ability.to_projectile(*id, position.0, direction),
                         replicate: Replicate::default(),
                     });
                 }
diff --git a/src/shared.rs b/src/shared.rs
index a7a2d16..6f01f85 100644
--- a/src/shared.rs
+++ b/src/shared.rs
@@ -9,6 +9,7 @@ use serde::Deserialize;
 use serde::Serialize;
 use std::default::Default;
 
+pub mod ability;
 pub mod champion;
 pub mod cooldown;
 pub mod health;
diff --git a/src/shared/ability.rs b/src/shared/ability.rs
new file mode 100644
index 0000000..8ef0c29
--- /dev/null
+++ b/src/shared/ability.rs
@@ -0,0 +1,65 @@
+use crate::shared::projectile::*;
+use crate::shared::*;
+
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub enum Ability {
+    Targeted(TargetedAbility),
+    Directional(DirectionalAbility),
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub enum TargetedAbility {
+    MeeleAttack,
+    RangedAttack,
+}
+
+impl TargetedAbility {
+    pub fn to_projectile(
+        self,
+        source_player: PlayerId,
+        position: Vec2,
+        target_player: PlayerId,
+    ) -> Projectile {
+        match self {
+            TargetedAbility::MeeleAttack => Projectile {
+                type_: ProjectileType::Targeted(TargetedProjectile {
+                    target_player,
+                    position,
+                }),
+                source_player,
+                damage: 5.,
+            },
+            TargetedAbility::RangedAttack => Projectile {
+                type_: ProjectileType::Instant(InstantProjectile { target_player }),
+                source_player,
+                damage: 6.,
+            },
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub enum DirectionalAbility {
+    Spear,
+}
+
+impl DirectionalAbility {
+    pub fn to_projectile(
+        self,
+        source_player: PlayerId,
+        position: Vec2,
+        direction: Vec2,
+    ) -> Projectile {
+        match self {
+            DirectionalAbility::Spear => Projectile {
+                type_: ProjectileType::Free(FreeProjectile {
+                    position,
+                    direction,
+                    starting_position: position,
+                }),
+                source_player,
+                damage: 15.,
+            },
+        }
+    }
+}
diff --git a/src/shared/champion.rs b/src/shared/champion.rs
index ec27c62..8de12a5 100644
--- a/src/shared/champion.rs
+++ b/src/shared/champion.rs
@@ -1,3 +1,4 @@
+use crate::shared::ability::*;
 use crate::shared::*;
 use std::str::FromStr;
 
@@ -37,3 +38,18 @@ impl Stats {
         }
     }
 }
+
+impl Champion {
+    pub fn to_ability(self, attack_key: AttackKey) -> Ability {
+        match self {
+            Champion::Meele => match attack_key {
+                AttackKey::Q => Ability::Directional(DirectionalAbility::Spear),
+                _ => Ability::Targeted(TargetedAbility::MeeleAttack),
+            },
+            Champion::Ranged => match attack_key {
+                AttackKey::Q => Ability::Directional(DirectionalAbility::Spear),
+                _ => Ability::Targeted(TargetedAbility::RangedAttack),
+            },
+        }
+    }
+}
diff --git a/src/shared/imperative.rs b/src/shared/imperative.rs
index 61a4762..886355f 100644
--- a/src/shared/imperative.rs
+++ b/src/shared/imperative.rs
@@ -1,3 +1,4 @@
+use crate::shared::ability::*;
 use crate::shared::*;
 use serde::Deserialize;
 use serde::Serialize;
@@ -6,8 +7,8 @@ use serde::Serialize;
 pub enum Imperative {
     Idle,
     WalkTo(Vec2),
-    AttackTarget(AttackKey, PlayerId),
-    AttackDirection(AttackKey, Vec2),
+    AttackTarget(TargetedAbility, PlayerId),
+    AttackDirection(DirectionalAbility, Vec2),
 }
 
 #[derive(Resource, Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
-- 
cgit v1.2.3