aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-18 04:58:00 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-18 07:02:29 +0100
commitec17616f8d6041ac5d93a786edec0f5d0f969a46 (patch)
treec3a1738aa715f33438591904310c1fca3f475349
parent937e1ed8d240c1a5b42f5b8cf1104321b9d79c4a (diff)
feat: speed ability
-rw-r--r--src/client.rs83
-rw-r--r--src/protocol.rs4
-rw-r--r--src/server.rs153
-rw-r--r--src/shared.rs3
-rw-r--r--src/shared/ability.rs8
-rw-r--r--src/shared/activation.rs16
-rw-r--r--src/shared/buffs.rs7
-rw-r--r--src/shared/champion.rs15
-rw-r--r--src/shared/imperative.rs2
-rw-r--r--src/shared/player.rs15
-rw-r--r--src/shared/stats.rs22
11 files changed, 249 insertions, 79 deletions
diff --git a/src/client.rs b/src/client.rs
index 7ccced7..3679b85 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,6 +1,7 @@
use crate::client::network::*;
use crate::protocol::*;
use crate::shared::ability::*;
+use crate::shared::activation::*;
use crate::shared::champion::*;
use crate::shared::cooldown::*;
use crate::shared::health::*;
@@ -8,6 +9,7 @@ use crate::shared::health_event::*;
use crate::shared::imperative::*;
use crate::shared::player::*;
use crate::shared::projectile::*;
+use crate::shared::stats::*;
use crate::shared::*;
use bevy::input::keyboard::*;
use bevy::input::mouse::MouseButton;
@@ -255,7 +257,8 @@ fn buffer_input(
) {
if mouse_input.just_pressed(MouseButton::Left) {
match attack.0 {
- Some(attack_key) => match champion.0.to_ability(attack_key) {
+ Some(ability_slot) => match champion.0.to_ability(ability_slot) {
+ Ability::Activated(_) => {}
Ability::Directional(_) => {
let Some(world_position) = cursor_world_position(&windows, &cameras) else {
return;
@@ -267,7 +270,8 @@ fn buffer_input(
return;
};
client.add_input(Inputs::Imperative(Imperative::AttackDirection(
- attack_key, direction,
+ ability_slot,
+ direction,
)));
attack.0 = None;
}
@@ -278,7 +282,7 @@ fn buffer_input(
return;
};
client.add_input(Inputs::Imperative(Imperative::AttackTarget(
- attack_key,
+ ability_slot,
target_player,
)));
attack.0 = None;
@@ -299,26 +303,45 @@ fn buffer_input(
}
}
}
+ } else {
+ client.add_input(Inputs::None);
}
}
-fn choose_attack(keyboard_input: Res<ButtonInput<KeyCode>>, mut attack_key: ResMut<Attack>) {
+fn choose_attack(
+ champion: Res<MyChampion>,
+ keyboard_input: Res<ButtonInput<KeyCode>>,
+ mut attack: ResMut<Attack>,
+ mut client: ClientMut,
+) {
if keyboard_input.just_pressed(KeyCode::KeyA) {
- attack_key.0 = Some(AbilitySlot::A);
+ attack.0 = Some(AbilitySlot::A);
} else if keyboard_input.just_pressed(KeyCode::KeyQ) {
- attack_key.0 = Some(AbilitySlot::Q);
+ attack.0 = Some(AbilitySlot::Q);
} else if keyboard_input.just_pressed(KeyCode::KeyW) {
- attack_key.0 = Some(AbilitySlot::W);
+ attack.0 = Some(AbilitySlot::W);
} else if keyboard_input.just_pressed(KeyCode::KeyE) {
- attack_key.0 = Some(AbilitySlot::E);
+ attack.0 = Some(AbilitySlot::E);
} else if keyboard_input.just_pressed(KeyCode::KeyR) {
- attack_key.0 = Some(AbilitySlot::R);
+ attack.0 = Some(AbilitySlot::R);
} else if keyboard_input.just_pressed(KeyCode::KeyF) {
- attack_key.0 = Some(AbilitySlot::F);
+ attack.0 = Some(AbilitySlot::F);
} else if keyboard_input.just_pressed(KeyCode::KeyG) {
- attack_key.0 = Some(AbilitySlot::G);
+ attack.0 = Some(AbilitySlot::G);
} else if keyboard_input.just_pressed(KeyCode::Escape) {
- attack_key.0 = None;
+ attack.0 = None;
+ } else if keyboard_input.just_pressed(KeyCode::ShiftLeft) {
+ attack.0 = None;
+ }
+ match attack.0 {
+ Some(ability_slot) => match champion.0.to_ability(ability_slot) {
+ Ability::Activated(_) => {
+ client.add_input(Inputs::Activation(Activation::Activate(ability_slot)));
+ attack.0 = None;
+ }
+ _ => {}
+ },
+ None => {}
}
}
@@ -383,6 +406,7 @@ fn gizmos_attack_indicator(
attack: Res<Attack>,
cameras: Query<(&Camera, &GlobalTransform)>,
client_id: Res<ClientId>,
+ effective_statses: Query<(&PlayerId, &EffectiveStats)>,
hoverables: Query<(&PlayerId, &PlayerPosition)>,
mut gizmos: Gizmos,
player_champions: Query<(&PlayerId, &Champion)>,
@@ -395,19 +419,16 @@ fn gizmos_attack_indicator(
let Some(champion) = player_champion(&client_id, &player_champions) else {
return;
};
- let Some(attack_key) = attack.0.or_else(|| {
+ let Some(ability_slot) = attack.0.or_else(|| {
hovered_other_player(&cameras, &client_id, &hoverables, &windows).map(|_| AbilitySlot::A)
}) else {
return;
};
- match champion.to_ability(attack_key) {
- Ability::Targeted(_) => {
- gizmos.circle_2d(
- position.0,
- Stats::from_champion(champion).attack_range,
- Color::YELLOW,
- );
- }
+ let Some(effective_stats) = player_effective_stats(&client_id, &effective_statses) else {
+ return;
+ };
+ match champion.to_ability(ability_slot) {
+ Ability::Activated(_) => {}
Ability::Directional(_) => {
let Some(world_position) = cursor_world_position(&windows, &cameras) else {
return;
@@ -417,6 +438,9 @@ fn gizmos_attack_indicator(
};
gizmos.arrow_2d(position.0, position.0 + 75. * direction, Color::YELLOW);
}
+ Ability::Targeted(_) => {
+ gizmos.circle_2d(position.0, effective_stats.0.attack_range, Color::YELLOW);
+ }
}
}
@@ -432,6 +456,18 @@ fn player_position(
None
}
+fn player_effective_stats(
+ client_id: &Res<ClientId>,
+ players: &Query<(&PlayerId, &EffectiveStats)>,
+) -> Option<EffectiveStats> {
+ for (id, effective_stats) in players.iter() {
+ if id.0 == client_id.0 {
+ return Some(*effective_stats);
+ }
+ }
+ None
+}
+
fn player_champion(
client_id: &Res<ClientId>,
players: &Query<(&PlayerId, &Champion)>,
@@ -506,11 +542,8 @@ fn hotbar_hotbar_display(
}
fn hotbar_hotbar_highlight(attack: Res<Attack>, mut hotbars: Query<(&Hotbar, &mut Text)>) {
- let Some(ability_slot) = attack.0 else {
- return;
- };
for (hotbar, mut text) in hotbars.iter_mut() {
- let is_highlighted = ability_slot == hotbar.0;
+ let is_highlighted = attack.0 == Some(hotbar.0);
if text.sections.len() <= 0 {
continue;
}
diff --git a/src/protocol.rs b/src/protocol.rs
index 1b7eaa0..f8f1f62 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -1,3 +1,4 @@
+use crate::shared::activation::*;
use crate::shared::champion::*;
use crate::shared::cooldown::*;
use crate::shared::health::*;
@@ -5,6 +6,7 @@ use crate::shared::health_event::*;
use crate::shared::imperative::*;
use crate::shared::player::*;
use crate::shared::projectile::*;
+use crate::shared::stats::*;
use bevy::prelude::*;
use lightyear::prelude::*;
use serde::Deserialize;
@@ -13,6 +15,7 @@ use serde::Serialize;
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum Inputs {
Imperative(Imperative),
+ Activation(Activation),
None,
}
impl UserAction for Inputs {}
@@ -38,6 +41,7 @@ pub enum Components {
Cooldown(Cooldown),
Health(Health),
Champion(Champion),
+ EffectiveStats(EffectiveStats),
}
#[derive(Channel)]
diff --git a/src/server.rs b/src/server.rs
index 038f854..0b15c5f 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -1,6 +1,8 @@
use crate::protocol::*;
use crate::server::network::*;
use crate::shared::ability::*;
+use crate::shared::activation::*;
+use crate::shared::buffs::*;
use crate::shared::champion::*;
use crate::shared::cooldown::*;
use crate::shared::health::*;
@@ -8,6 +10,7 @@ use crate::shared::health_event::*;
use crate::shared::imperative::*;
use crate::shared::player::*;
use crate::shared::projectile::*;
+use crate::shared::stats::*;
use crate::shared::*;
use bevy::prelude::*;
use bevy::utils::HashMap;
@@ -53,13 +56,15 @@ impl Plugin for ServerPlugin {
.add_systems(Update, (connects, disconnects))
.add_systems(Update, receive_message)
.add_systems(FixedUpdate, timers_tick)
+ .add_systems(FixedUpdate, effective_stats)
.add_systems(FixedUpdate, health_regen.after(timers_tick))
.add_systems(
FixedUpdate,
(
(imperative_attack_approach, imperative_attack_attack)
.chain()
- .after(cooldown_decrement),
+ .after(activation),
+ activation.after(cooldown_decrement),
imperative_walk_to,
)
.after(player_input),
@@ -73,6 +78,7 @@ impl Plugin for ServerPlugin {
.chain(),
)
.add_systems(FixedUpdate, cooldown_decrement)
+ .add_systems(FixedUpdate, (buffs_despawn, buffs_tick).chain())
.add_systems(FixedUpdate, player_input);
}
}
@@ -110,7 +116,7 @@ fn receive_message(
let client_id = event.context();
let SelectChampion(champion) = event.message();
let Some(entity) = entity_map.0.get(client_id) else {
- return;
+ continue;
};
commands.entity(*entity).insert(*champion);
}
@@ -133,6 +139,7 @@ fn player_input(
entity_map: Res<EntityMap>,
mut input_reader: EventReader<server::InputEvent<Inputs>>,
mut imperatives: Query<&mut Imperative>,
+ mut activations: Query<&mut Activation>,
) {
for input in input_reader.read() {
let client_id = input.context();
@@ -144,28 +151,31 @@ fn player_input(
*imperative = *new_imperative;
}
}
- _ => {}
+ Inputs::Activation(new_activation) => {
+ if let Ok(mut activation) = activations.get_mut(*entity_id) {
+ *activation = *new_activation;
+ }
+ }
+ Inputs::None => {}
}
}
}
}
}
-const MOVEMENT_SPEED: f32 = 80.;
-
fn imperative_walk_to(
- mut players: Query<(Entity, &mut Imperative)>,
+ mut players: Query<(Entity, &mut Imperative, &EffectiveStats)>,
mut positions: Query<&mut PlayerPosition>,
time: Res<Time>,
) {
- for (entity, mut imperative) in players.iter_mut() {
+ for (entity, mut imperative, effective_stats) in players.iter_mut() {
match *imperative {
Imperative::WalkTo(target_position) => {
if let Ok(mut position) = positions.get_mut(entity) {
let (new_position, target_reached) = move_to_target(
position.0,
target_position,
- MOVEMENT_SPEED * time.delta().as_secs_f32(),
+ effective_stats.0.movement_speed * time.delta().as_secs_f32(),
);
position.0 = new_position;
if target_reached {
@@ -197,38 +207,33 @@ fn move_along_direction(position: Vec2, direction: Vec2, distance: f32) -> Vec2
fn imperative_attack_approach(
entity_map: Res<EntityMap>,
- mut players: Query<(&PlayerId, &mut Imperative)>,
+ mut players: Query<(&PlayerId, &mut Imperative, &EffectiveStats)>,
mut positions: Query<&mut PlayerPosition>,
- champions: Query<&Champion>,
time: Res<Time>,
) {
- for (id, mut imperative) in players.iter_mut() {
+ for (id, mut imperative, effective_stats) in players.iter_mut() {
match *imperative {
Imperative::AttackTarget(_, target_player) => {
let Some(entity) = entity_map.0.get(&id.0) else {
*imperative = Imperative::Idle;
- return;
- };
- let Ok(champion) = champions.get(*entity) else {
- *imperative = Imperative::Idle;
- return;
+ continue;
};
let Some(target_entity) = entity_map.0.get(&target_player.0) else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let Ok([mut position, target_position]) =
positions.get_many_mut([*entity, *target_entity])
else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let distance = target_position.0.distance(position.0);
- if distance > Stats::from_champion(*champion).attack_range {
+ if distance > effective_stats.0.attack_range {
let (new_position, _) = move_to_target(
position.0,
target_position.0,
- MOVEMENT_SPEED * time.delta().as_secs_f32(),
+ effective_stats.0.movement_speed * time.delta().as_secs_f32(),
);
position.0 = new_position;
}
@@ -242,40 +247,40 @@ fn imperative_attack_attack(
entity_map: Res<EntityMap>,
mut commands: Commands,
mut cooldowns: Query<&mut Cooldown>,
- mut players: Query<(&PlayerId, &mut Imperative)>,
+ mut players: Query<(&PlayerId, &mut Imperative, &EffectiveStats)>,
mut positions: Query<&mut PlayerPosition>,
champions: Query<&Champion>,
) {
- for (id, mut imperative) in players.iter_mut() {
+ for (id, mut imperative, effective_stats) in players.iter_mut() {
let Some(entity) = entity_map.0.get(&id.0) else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let Ok(champion) = champions.get(*entity) else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
match *imperative {
Imperative::AttackTarget(ability_slot, target_player) => {
let Ability::Targeted(ability) = champion.to_ability(ability_slot) else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let Some(target_entity) = entity_map.0.get(&target_player.0) else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let Ok([position, target_position]) =
positions.get_many_mut([*entity, *target_entity])
else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let distance = target_position.0.distance(position.0);
- if distance <= Stats::from_champion(*champion).attack_range {
+ if distance <= effective_stats.0.attack_range {
let Ok(mut cooldown) = cooldowns.get_mut(*entity) else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let base_cooldown = BaseCooldown::from_champion(*champion);
if cooldown.0[ability_slot].is_zero() {
@@ -285,21 +290,24 @@ fn imperative_attack_attack(
position.0,
target_player,
)));
+ if ability_slot != AbilitySlot::A {
+ *imperative = Imperative::Idle;
+ }
}
}
}
Imperative::AttackDirection(ability_slot, direction) => {
let Ability::Directional(ability) = champion.to_ability(ability_slot) else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let Ok(position) = positions.get_mut(*entity) else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let Ok(mut cooldown) = cooldowns.get_mut(*entity) else {
*imperative = Imperative::Idle;
- return;
+ continue;
};
let base_cooldown = BaseCooldown::from_champion(*champion);
if cooldown.0[ability_slot].is_zero() {
@@ -307,6 +315,7 @@ fn imperative_attack_attack(
commands.spawn(ProjectileBundle::new(
ability.to_projectile(*id, position.0, direction),
));
+ *imperative = Imperative::Idle;
}
}
_ => {}
@@ -314,6 +323,46 @@ fn imperative_attack_attack(
}
}
+fn activation(
+ entity_map: Res<EntityMap>,
+ mut cooldowns: Query<&mut Cooldown>,
+ mut players: Query<(&PlayerId, &mut Activation, &mut Buffs)>,
+ champions: Query<&Champion>,
+) {
+ for (id, mut activation, mut buffs) in players.iter_mut() {
+ let Some(entity) = entity_map.0.get(&id.0) else {
+ *activation = Activation::None;
+ continue;
+ };
+ let Ok(champion) = champions.get(*entity) else {
+ *activation = Activation::None;
+ continue;
+ };
+ let Activation::Activate(ability_slot) = *activation else {
+ *activation = Activation::None;
+ continue;
+ };
+ let Ability::Activated(ability) = champion.to_ability(ability_slot) else {
+ *activation = Activation::None;
+ continue;
+ };
+ let Ok(mut cooldown) = cooldowns.get_mut(*entity) else {
+ *activation = Activation::None;
+ continue;
+ };
+ match ability {
+ ActivatedAbility::Speed => {
+ let base_cooldown = BaseCooldown::from_champion(*champion);
+ 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));
+ *activation = Activation::None;
+ }
+ }
+ }
+ }
+}
+
const PROJECTILE_SPEED: f32 = 150.;
fn projectile_move(
@@ -460,3 +509,43 @@ fn health_regen(health_regen_timer: Res<HealthRegenTimer>, mut healths: Query<&m
}
}
}
+
+fn effective_stats(mut effective_statses: Query<(&Champion, &mut EffectiveStats, &Buffs)>) {
+ for (champion, mut effective_stats, buffs) in effective_statses.iter_mut() {
+ let mut stats = BaseStats::from_champion(*champion).0;
+ if buffs.slow.is_some() {
+ stats.movement_speed *= 0.85;
+ }
+ if buffs.speed.is_some() {
+ stats.movement_speed *= 1.25;
+ }
+ effective_stats.0 = stats;
+ }
+}
+
+fn buffs_tick(mut buffses: Query<&mut Buffs>, time: Res<Time>) {
+ let dt = time.delta();
+ for mut buffs in buffses.iter_mut() {
+ if let Some(ref mut timer) = &mut buffs.slow {
+ timer.tick(dt);
+ }
+ if let Some(ref mut timer) = &mut buffs.speed {
+ timer.tick(dt);
+ }
+ }
+}
+
+fn buffs_despawn(mut buffses: Query<&mut Buffs>) {
+ for mut buffs in buffses.iter_mut() {
+ if let Some(timer) = &buffs.slow {
+ if timer.finished() {
+ buffs.slow = None;
+ }
+ }
+ if let Some(timer) = &buffs.speed {
+ if timer.finished() {
+ buffs.speed = None;
+ }
+ }
+ }
+}
diff --git a/src/shared.rs b/src/shared.rs
index a06ff3e..678c592 100644
--- a/src/shared.rs
+++ b/src/shared.rs
@@ -10,6 +10,8 @@ use serde::Serialize;
use std::default::Default;
pub mod ability;
+pub mod activation;
+pub mod buffs;
pub mod champion;
pub mod cooldown;
pub mod health;
@@ -17,6 +19,7 @@ pub mod health_event;
pub mod imperative;
pub mod player;
pub mod projectile;
+pub mod stats;
pub const KEY: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
diff --git a/src/shared/ability.rs b/src/shared/ability.rs
index cab7ae6..e776015 100644
--- a/src/shared/ability.rs
+++ b/src/shared/ability.rs
@@ -6,8 +6,9 @@ use std::ops::*;
#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
pub enum Ability {
- Targeted(TargetedAbility),
+ Activated(ActivatedAbility),
Directional(DirectionalAbility),
+ Targeted(TargetedAbility),
}
#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
@@ -42,6 +43,11 @@ impl TargetedAbility {
}
#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub enum ActivatedAbility {
+ Speed,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
pub enum DirectionalAbility {
Spear,
}
diff --git a/src/shared/activation.rs b/src/shared/activation.rs
new file mode 100644
index 0000000..3ca6956
--- /dev/null
+++ b/src/shared/activation.rs
@@ -0,0 +1,16 @@
+use crate::shared::ability::*;
+use crate::shared::*;
+use serde::Deserialize;
+use serde::Serialize;
+
+#[derive(Component, Message, Copy, Clone, PartialEq, Debug, Deserialize, Serialize)]
+pub enum Activation {
+ Activate(AbilitySlot),
+ None,
+}
+
+impl Default for Activation {
+ fn default() -> Self {
+ Activation::None
+ }
+}
diff --git a/src/shared/buffs.rs b/src/shared/buffs.rs
new file mode 100644
index 0000000..4fe033d
--- /dev/null
+++ b/src/shared/buffs.rs
@@ -0,0 +1,7 @@
+use crate::shared::*;
+
+#[derive(Clone, Component, Default, Debug)]
+pub struct Buffs {
+ pub slow: Option<Timer>,
+ pub speed: Option<Timer>,
+}
diff --git a/src/shared/champion.rs b/src/shared/champion.rs
index 28d1ff2..c32d3d5 100644
--- a/src/shared/champion.rs
+++ b/src/shared/champion.rs
@@ -26,28 +26,17 @@ impl FromStr for Champion {
}
}
-pub struct Stats {
- pub attack_range: f32,
-}
-
-impl Stats {
- pub fn from_champion(champion: Champion) -> Self {
- match champion {
- Champion::Meele => Stats { attack_range: 25. },
- Champion::Ranged => Stats { attack_range: 60. },
- }
- }
-}
-
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::G => Ability::Activated(ActivatedAbility::Speed),
_ => Ability::Targeted(TargetedAbility::MeeleAttack),
},
Champion::Ranged => match ability_slot {
AbilitySlot::Q => Ability::Directional(DirectionalAbility::Spear),
+ AbilitySlot::G => Ability::Activated(ActivatedAbility::Speed),
_ => Ability::Targeted(TargetedAbility::RangedAttack),
},
}
diff --git a/src/shared/imperative.rs b/src/shared/imperative.rs
index 44a0e43..4291368 100644
--- a/src/shared/imperative.rs
+++ b/src/shared/imperative.rs
@@ -8,6 +8,6 @@ use serde::Serialize;
pub enum Imperative {
Idle,
WalkTo(Vec2),
- AttackTarget(AbilitySlot, PlayerId),
AttackDirection(AbilitySlot, Vec2),
+ AttackTarget(AbilitySlot, PlayerId),
}
diff --git a/src/shared/player.rs b/src/shared/player.rs
index a886499..4f571d8 100644
--- a/src/shared/player.rs
+++ b/src/shared/player.rs
@@ -1,3 +1,6 @@
+use crate::shared::activation::*;
+use crate::shared::buffs::*;
+use crate::shared::stats::*;
use crate::shared::*;
#[derive(Bundle)]
@@ -9,6 +12,9 @@ pub struct PlayerBundle {
cooldown: Cooldown,
health: Health,
champion: Champion,
+ effective_stats: EffectiveStats,
+ buffs: Buffs,
+ activation: Activation,
replicate: Replicate,
}
@@ -20,8 +26,10 @@ impl PlayerBundle {
};
replicate.enable_replicate_once::<PlayerId>();
replicate.enable_replicate_once::<PlayerColor>();
- replicate.target::<Cooldown>(NetworkTarget::Single(id));
replicate.target::<Champion>(NetworkTarget::Single(id));
+ replicate.target::<Cooldown>(NetworkTarget::Single(id));
+ replicate.target::<EffectiveStats>(NetworkTarget::Single(id));
+ let champion = Champion::default();
PlayerBundle {
id: PlayerId(id),
position: PlayerPosition(position),
@@ -29,7 +37,10 @@ impl PlayerBundle {
imperative: Imperative::Idle,
cooldown: Cooldown::default(),
health: Health::default(),
- champion: Champion::default(),
+ champion,
+ effective_stats: EffectiveStats(BaseStats::from_champion(champion).0),
+ buffs: Buffs::default(),
+ activation: Activation::default(),
replicate,
}
}
diff --git a/src/shared/stats.rs b/src/shared/stats.rs
index 278a19f..749453c 100644
--- a/src/shared/stats.rs
+++ b/src/shared/stats.rs
@@ -1,16 +1,28 @@
-use crate::shared::champion::*;
use crate::shared::*;
-#[derive(Component, Message, Clone, Serialize, Deserialize, PartialEq)]
+#[derive(Clone, Copy, Serialize, Deserialize, PartialEq)]
pub struct Stats {
pub attack_range: f32,
+ pub movement_speed: f32,
}
-impl Stats {
+#[derive(Component, Message, Clone, Copy, Serialize, Deserialize, PartialEq)]
+pub struct BaseStats(pub Stats);
+
+#[derive(Component, Message, Clone, Copy, Serialize, Deserialize, PartialEq)]
+pub struct EffectiveStats(pub Stats);
+
+impl BaseStats {
pub fn from_champion(champion: Champion) -> Self {
match champion {
- Champion::Meele => Stats { attack_range: 35. },
- Champion::Ranged => Stats { attack_range: 60. },
+ Champion::Meele => BaseStats(Stats {
+ attack_range: 25.,
+ movement_speed: 75.,
+ }),
+ Champion::Ranged => BaseStats(Stats {
+ attack_range: 60.,
+ movement_speed: 85.,
+ }),
}
}
}