From e9dce64b009455163f2a262ed481c37300917c4a Mon Sep 17 00:00:00 2001
From: Alexander Foremny <aforemny@posteo.de>
Date: Tue, 19 Mar 2024 05:50:53 +0100
Subject: chore: tweak champions

---
 src/client.rs            |  86 ++++++------
 src/server.rs            |  38 +++---
 src/shared/ability.rs    | 348 ++++++++++++++++++++++++++---------------------
 src/shared/champion.rs   |  58 ++++++--
 src/shared/cooldown.rs   |  13 --
 src/shared/player.rs     |   2 +-
 src/shared/projectile.rs |   1 +
 src/shared/stats.rs      |  17 ---
 8 files changed, 305 insertions(+), 258 deletions(-)

diff --git a/src/client.rs b/src/client.rs
index 1768b2e..5084eaa 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -257,7 +257,7 @@ fn buffer_input(
 ) {
     if mouse_input.just_pressed(MouseButton::Left) {
         match attack.0 {
-            Some(ability_slot) => match champion.0.to_ability(ability_slot) {
+            Some(ability_slot) => match champion.0.ability(ability_slot) {
                 Ability::Activated(_) => {}
                 Ability::Directional(_) => {
                     let Some(world_position) = cursor_world_position(&windows, &cameras) else {
@@ -334,7 +334,7 @@ fn choose_attack(
         attack.0 = None;
     }
     match attack.0 {
-        Some(ability_slot) => match champion.0.to_ability(ability_slot) {
+        Some(ability_slot) => match champion.0.ability(ability_slot) {
             Ability::Activated(_) => {
                 client.add_input(Inputs::Activation(Activation::Activate(ability_slot)));
                 attack.0 = None;
@@ -427,56 +427,52 @@ fn gizmos_attack_indicator(
     let Some(effective_stats) = player_effective_stats(&client_id, &effective_statses) else {
         return;
     };
-    match champion.to_ability(ability_slot) {
+    match champion.ability(ability_slot) {
         Ability::Activated(_) => {}
-        Ability::Directional(DirectionalAbility::Dash) => {
+        Ability::Directional(ability) => {
             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(DirectionalAbility::Pull) => {
-            let Some(world_position) = cursor_world_position(&windows, &cameras) else {
-                return;
-            };
-            let Some(direction) = (world_position - position.0).try_normalize() else {
-                return;
-            };
-            let Some((_, pull_start, pull_end)) = pull_collision(
-                PlayerId(client_id.0),
-                position.0,
-                direction,
-                150.,
-                &player_positions,
-            ) else {
-                let pull_direction = -150. * direction;
-                let pull_start = position.0 - pull_direction;
-                let pull_end = pull_start
-                    + (pull_direction.length() - 2. * PLAYER_RADIUS)
-                        * pull_direction.normalize_or_zero();
-                gizmos.arrow_2d(pull_start, pull_end, Color::YELLOW);
-                return;
-            };
-            gizmos.arrow_2d(pull_start, pull_end, Color::YELLOW);
-        }
-        Ability::Directional(_) => {
-            let Some(world_position) = cursor_world_position(&windows, &cameras) else {
-                return;
-            };
-            let Some(direction) = (world_position - position.0).try_normalize() else {
-                return;
-            };
-            gizmos.arrow_2d(position.0, position.0 + 75. * direction, Color::YELLOW);
+            match ability {
+                DirectionalAbility::Dash(Dash { max_distance }) => {
+                    let dash_end = dash_collision(
+                        PlayerId(client_id.0),
+                        position.0,
+                        direction,
+                        max_distance,
+                        &player_positions,
+                    );
+                    gizmos.arrow_2d(position.0, dash_end, Color::YELLOW);
+                }
+                DirectionalAbility::Pull(Pull { max_distance }) => {
+                    let Some((_, pull_start, pull_end)) = pull_collision(
+                        PlayerId(client_id.0),
+                        position.0,
+                        direction,
+                        max_distance,
+                        &player_positions,
+                    ) else {
+                        let pull_direction = -max_distance * direction;
+                        let pull_start = position.0 - pull_direction;
+                        let pull_end = pull_start
+                            + (pull_direction.length() - 2. * PLAYER_RADIUS)
+                                * pull_direction.normalize_or_zero();
+                        gizmos.arrow_2d(pull_start, pull_end, Color::YELLOW);
+                        return;
+                    };
+                    gizmos.arrow_2d(pull_start, pull_end, Color::YELLOW);
+                }
+                DirectionalAbility::Spear(Spear { max_distance, .. }) => {
+                    gizmos.arrow_2d(
+                        position.0,
+                        position.0 + max_distance * direction,
+                        Color::YELLOW,
+                    );
+                }
+            }
         }
         Ability::Targeted(_) => {
             gizmos.circle_2d(position.0, effective_stats.0.attack_range, Color::YELLOW);
diff --git a/src/server.rs b/src/server.rs
index 8507244..b3c2a30 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -262,7 +262,7 @@ fn imperative_attack_attack(
         };
         match *imperative {
             Imperative::AttackTarget(ability_slot, target_player) => {
-                let Ability::Targeted(ability) = champion.to_ability(ability_slot) else {
+                let Ability::Targeted(ability) = champion.ability(ability_slot) else {
                     *imperative = Imperative::Idle;
                     continue;
                 };
@@ -282,7 +282,7 @@ fn imperative_attack_attack(
                         *imperative = Imperative::Idle;
                         continue;
                     };
-                    let base_cooldown = BaseCooldown::from_champion(*champion);
+                    let base_cooldown = champion.base_cooldown();
                     if cooldown.0[ability_slot].is_zero() {
                         cooldown.0[ability_slot] = base_cooldown.0[ability_slot];
                         commands.spawn(ProjectileBundle::new(ability.to_projectile(
@@ -297,24 +297,20 @@ fn imperative_attack_attack(
                 }
             }
             Imperative::AttackDirection(ability_slot, direction) => {
-                let Ability::Directional(ability) = champion.to_ability(ability_slot) else {
+                let Ability::Directional(ability) = champion.ability(ability_slot) else {
                     *imperative = Imperative::Idle;
                     continue;
                 };
-                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;
-                    }
+                let Ok(mut cooldown) = cooldowns.get_mut(*entity) else {
+                    *imperative = Imperative::Idle;
+                    continue;
+                };
+                let base_cooldown = champion.base_cooldown();
+                if cooldown.0[ability_slot].is_zero() {
+                    cooldown.0[ability_slot] = base_cooldown.0[ability_slot];
+                    ability.activate()(&mut commands, *id, direction);
                 }
+                *imperative = Imperative::Idle;
             }
             _ => {}
         }
@@ -340,7 +336,7 @@ fn activation(
             *activation = Activation::None;
             continue;
         };
-        let Ability::Activated(ability) = champion.to_ability(ability_slot) else {
+        let Ability::Activated(ability) = champion.ability(ability_slot) else {
             *activation = Activation::None;
             continue;
         };
@@ -349,11 +345,11 @@ fn activation(
             continue;
         };
         match ability {
-            ActivatedAbility::Speed => {
-                let base_cooldown = BaseCooldown::from_champion(*champion);
+            ActivatedAbility::Speed(Speed { duration }) => {
+                let base_cooldown = champion.base_cooldown();
                 if cooldown.0[ability_slot].is_zero() {
                     cooldown.0[ability_slot] = base_cooldown.0[ability_slot];
-                    buffs.speed = Some(Timer::from_seconds(2., TimerMode::Once));
+                    buffs.speed = Some(Timer::from_seconds(duration, TimerMode::Once));
                     *activation = Activation::None;
                 }
             }
@@ -524,7 +520,7 @@ fn effective_stats(
     mut effective_statses: Query<(&Champion, &mut EffectiveStats, &Buffs, &mut Health)>,
 ) {
     for (champion, mut effective_stats, buffs, mut health) in effective_statses.iter_mut() {
-        let mut stats = BaseStats::from_champion(*champion).0;
+        let mut stats = champion.base_stats().0;
         if buffs.slow.is_some() {
             stats.movement_speed *= 0.85;
         }
diff --git a/src/shared/ability.rs b/src/shared/ability.rs
index 2c581e7..4d45a4d 100644
--- a/src/shared/ability.rs
+++ b/src/shared/ability.rs
@@ -14,8 +14,18 @@ pub enum Ability {
 
 #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
 pub enum TargetedAbility {
-    MeeleAttack,
-    RangedAttack,
+    MeeleAttack(MeeleAttack),
+    RangedAttack(RangedAttack),
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub struct MeeleAttack {
+    pub damage: f32,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub struct RangedAttack {
+    pub damage: f32,
 }
 
 impl TargetedAbility {
@@ -26,18 +36,18 @@ impl TargetedAbility {
         target_player: PlayerId,
     ) -> Projectile {
         match self {
-            TargetedAbility::MeeleAttack => Projectile {
+            TargetedAbility::MeeleAttack(MeeleAttack { damage }) => Projectile {
                 type_: ProjectileType::Instant(InstantProjectile { target_player }),
                 source_player,
-                damage: 5.,
+                damage,
             },
-            TargetedAbility::RangedAttack => Projectile {
+            TargetedAbility::RangedAttack(RangedAttack { damage }) => Projectile {
                 type_: ProjectileType::Targeted(TargetedProjectile {
                     target_player,
                     position,
                 }),
                 source_player,
-                damage: 6.,
+                damage,
             },
         }
     }
@@ -45,81 +55,104 @@ impl TargetedAbility {
 
 #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
 pub enum ActivatedAbility {
-    Speed,
+    Speed(Speed),
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub struct Speed {
+    pub duration: f32,
 }
 
 #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
 pub enum DirectionalAbility {
-    Dash,
-    Pull,
-    Spear,
+    Dash(Dash),
+    Pull(Pull),
+    Spear(Spear),
 }
 
-pub struct DirectionalAbilityActivation(
-    pub fn(commands: &mut Commands, source_player: PlayerId, direction: Vec2) -> (),
-);
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub struct Dash {
+    pub max_distance: f32,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub struct Pull {
+    pub max_distance: f32,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub struct Spear {
+    pub max_distance: f32,
+    pub damage: f32,
+}
+
+pub type DirectionalAbilityActivation = Box<dyn FnOnce(&mut Commands, PlayerId, Vec2) -> ()>;
 
 impl DirectionalAbility {
     pub fn activate(self) -> DirectionalAbilityActivation {
         match self {
-            DirectionalAbility::Dash => DirectionalAbilityActivation(dash_activation),
-            DirectionalAbility::Pull => DirectionalAbilityActivation(pull_activation),
-            DirectionalAbility::Spear => DirectionalAbilityActivation(spear_activation),
+            DirectionalAbility::Dash(dash) => dash_activation(dash),
+            DirectionalAbility::Pull(pull) => pull_activation(pull),
+            DirectionalAbility::Spear(spear) => spear_activation(spear),
         }
     }
 }
 
-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;
+fn dash_activation(dash: Dash) -> DirectionalAbilityActivation {
+    Box::new(
+        move |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,
+                                dash.max_distance,
+                                &dash_targets,
+                            )
+                        };
+
+                        let mut positions = set.p0();
+                        if let Ok(mut position) = positions.get_mut(source_entity) {
+                            position.0 = dash_end;
                         }
-                        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(
@@ -161,73 +194,77 @@ pub fn dash_collision(
     }
 }
 
-fn pull_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 Some((target_player, _, pull_end)) = ({
-                    let pull_targets = set.p1();
-                    pull_collision(
-                        source_player,
-                        source_position.0,
-                        direction,
-                        150.,
-                        &pull_targets,
-                    )
-                }) else {
-                    return;
-                };
-
-                let Some(target_entity) = ({
-                    let mut target_entity = None;
-                    for (entity, player_id) in players.iter() {
-                        if *player_id != target_player {
-                            continue;
+fn pull_activation(pull: Pull) -> DirectionalAbilityActivation {
+    Box::new(
+        move |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 Some((target_player, _, pull_end)) = ({
+                            let pull_targets = set.p1();
+                            pull_collision(
+                                source_player,
+                                source_position.0,
+                                direction,
+                                pull.max_distance,
+                                &pull_targets,
+                            )
+                        }) else {
+                            return;
+                        };
+
+                        let Some(target_entity) = ({
+                            let mut target_entity = None;
+                            for (entity, player_id) in players.iter() {
+                                if *player_id != target_player {
+                                    continue;
+                                }
+                                target_entity = Some(entity);
+                                break;
+                            }
+                            target_entity
+                        }) else {
+                            return;
+                        };
+
+                        let mut positions = set.p0();
+                        if let Ok(mut position) = positions.get_mut(target_entity) {
+                            position.0 = pull_end;
                         }
-                        target_entity = Some(entity);
-                        break;
-                    }
-                    target_entity
-                }) else {
-                    return;
-                };
-
-                let mut positions = set.p0();
-                if let Ok(mut position) = positions.get_mut(target_entity) {
-                    position.0 = pull_end;
-                }
-            },
-        )
-    });
+                    },
+                )
+            });
+        },
+    )
 }
 
 pub fn pull_collision(
@@ -281,27 +318,32 @@ pub fn pull_collision(
     }
 }
 
-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.,
-                    }));
-                }
-            },
-        )
-    });
+fn spear_activation(spear: Spear) -> DirectionalAbilityActivation {
+    Box::new(
+        move |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,
+                                    max_distance: spear.max_distance,
+                                }),
+                                source_player,
+                                damage: spear.damage,
+                            }));
+                        }
+                    },
+                )
+            });
+        },
+    )
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
diff --git a/src/shared/champion.rs b/src/shared/champion.rs
index ccc2fcd..05f13ef 100644
--- a/src/shared/champion.rs
+++ b/src/shared/champion.rs
@@ -1,5 +1,7 @@
 use crate::shared::ability::*;
+use crate::shared::stats::*;
 use crate::shared::*;
+use bevy::utils::*;
 use std::str::FromStr;
 
 #[derive(Component, Message, Clone, Copy, Serialize, Deserialize, PartialEq, Debug)]
@@ -27,19 +29,59 @@ impl FromStr for Champion {
 }
 
 impl Champion {
-    pub fn to_ability(self, ability_slot: AbilitySlot) -> Ability {
+    pub fn base_stats(self) -> BaseStats {
+        match self {
+            Champion::Meele => BaseStats(Stats {
+                attack_range: 25.,
+                movement_speed: 75.,
+                max_health: 150.,
+            }),
+            Champion::Ranged => BaseStats(Stats {
+                attack_range: 60.,
+                movement_speed: 85.,
+                max_health: 100.,
+            }),
+        }
+    }
+
+    pub fn ability(self, ability_slot: AbilitySlot) -> Ability {
         match self {
             Champion::Meele => match ability_slot {
-                AbilitySlot::Q => Ability::Directional(DirectionalAbility::Dash),
-                AbilitySlot::W => Ability::Directional(DirectionalAbility::Pull),
-                AbilitySlot::G => Ability::Activated(ActivatedAbility::Speed),
-                _ => Ability::Targeted(TargetedAbility::MeeleAttack),
+                AbilitySlot::Q => {
+                    Ability::Directional(DirectionalAbility::Dash(Dash { max_distance: 60. }))
+                }
+                AbilitySlot::W => {
+                    Ability::Directional(DirectionalAbility::Pull(Pull { max_distance: 60. }))
+                }
+                AbilitySlot::R => {
+                    Ability::Targeted(TargetedAbility::MeeleAttack(MeeleAttack { damage: 45. }))
+                }
+                AbilitySlot::G => {
+                    Ability::Activated(ActivatedAbility::Speed(Speed { duration: 2.5 }))
+                }
+                _ => Ability::Targeted(TargetedAbility::MeeleAttack(MeeleAttack { damage: 5. })),
             },
             Champion::Ranged => match ability_slot {
-                AbilitySlot::Q => Ability::Directional(DirectionalAbility::Spear),
-                AbilitySlot::G => Ability::Activated(ActivatedAbility::Speed),
-                _ => Ability::Targeted(TargetedAbility::RangedAttack),
+                AbilitySlot::Q => Ability::Directional(DirectionalAbility::Spear(Spear {
+                    max_distance: 250.,
+                    damage: 15.,
+                })),
+                AbilitySlot::G => {
+                    Ability::Activated(ActivatedAbility::Speed(Speed { duration: 2.5 }))
+                }
+                _ => Ability::Targeted(TargetedAbility::RangedAttack(RangedAttack { damage: 6. })),
             },
         }
     }
+
+    pub fn base_cooldown(self) -> BaseCooldown {
+        match self {
+            Champion::Meele => {
+                BaseCooldown([0.75, 5., 5., 10., 25., 50., 50.].map(Duration::from_secs_f32))
+            }
+            Champion::Ranged => {
+                BaseCooldown([1.25, 5., 5., 10., 25., 50., 50.].map(Duration::from_secs_f32))
+            }
+        }
+    }
 }
diff --git a/src/shared/cooldown.rs b/src/shared/cooldown.rs
index bf83e42..8f72520 100644
--- a/src/shared/cooldown.rs
+++ b/src/shared/cooldown.rs
@@ -8,16 +8,3 @@ use std::default::Default;
 pub struct Cooldown(pub [Duration; 7]);
 
 pub struct BaseCooldown(pub [Duration; 7]);
-
-impl BaseCooldown {
-    pub fn from_champion(champion: Champion) -> Self {
-        match champion {
-            Champion::Meele => {
-                BaseCooldown([0.75, 5., 5., 10., 25., 50., 50.].map(Duration::from_secs_f32))
-            }
-            Champion::Ranged => {
-                BaseCooldown([1.25, 5., 5., 10., 25., 50., 50.].map(Duration::from_secs_f32))
-            }
-        }
-    }
-}
diff --git a/src/shared/player.rs b/src/shared/player.rs
index c42f5cb..d1df3af 100644
--- a/src/shared/player.rs
+++ b/src/shared/player.rs
@@ -30,7 +30,7 @@ impl PlayerBundle {
         replicate.target::<Cooldown>(NetworkTarget::Single(id));
         replicate.target::<EffectiveStats>(NetworkTarget::Single(id));
         let champion = Champion::default();
-        let effective_stats = EffectiveStats(BaseStats::from_champion(champion).0);
+        let effective_stats = EffectiveStats(champion.base_stats().0);
         PlayerBundle {
             id: PlayerId(id),
             position: PlayerPosition(position),
diff --git a/src/shared/projectile.rs b/src/shared/projectile.rs
index 772f5d9..bc0a505 100644
--- a/src/shared/projectile.rs
+++ b/src/shared/projectile.rs
@@ -39,6 +39,7 @@ pub struct FreeProjectile {
     pub position: Vec2,
     pub direction: Vec2,
     pub starting_position: Vec2,
+    pub max_distance: f32,
 }
 
 #[derive(Component, Message, Serialize, Deserialize, Clone, Debug, PartialEq)]
diff --git a/src/shared/stats.rs b/src/shared/stats.rs
index ae449f0..bc9a509 100644
--- a/src/shared/stats.rs
+++ b/src/shared/stats.rs
@@ -12,20 +12,3 @@ pub struct BaseStats(pub Stats);
 
 #[derive(Component, Message, Clone, Copy, Serialize, Deserialize, PartialEq, Debug)]
 pub struct EffectiveStats(pub Stats);
-
-impl BaseStats {
-    pub fn from_champion(champion: Champion) -> Self {
-        match champion {
-            Champion::Meele => BaseStats(Stats {
-                attack_range: 25.,
-                movement_speed: 75.,
-                max_health: 150.,
-            }),
-            Champion::Ranged => BaseStats(Stats {
-                attack_range: 60.,
-                movement_speed: 85.,
-                max_health: 100.,
-            }),
-        }
-    }
-}
-- 
cgit v1.2.3