diff options
-rw-r--r-- | src/client.rs | 86 | ||||
-rw-r--r-- | src/server.rs | 38 | ||||
-rw-r--r-- | src/shared/ability.rs | 348 | ||||
-rw-r--r-- | src/shared/champion.rs | 58 | ||||
-rw-r--r-- | src/shared/cooldown.rs | 13 | ||||
-rw-r--r-- | src/shared/player.rs | 2 | ||||
-rw-r--r-- | src/shared/projectile.rs | 1 | ||||
-rw-r--r-- | 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., - }), - } - } -} |