diff options
author | Alexander Foremny <aforemny@posteo.de> | 2024-03-24 14:32:01 +0100 |
---|---|---|
committer | Alexander Foremny <aforemny@posteo.de> | 2024-03-24 14:34:41 +0100 |
commit | f7428e317d282856d05ffc37b6ba474c5e9973ce (patch) | |
tree | cf016b07c1145ad4b62c210eb739085c0639f407 | |
parent | 1751960338fc7a08fec438a9b53c78118a35063a (diff) |
feat: damage types
-rw-r--r-- | src/server.rs | 150 | ||||
-rw-r--r-- | src/shared.rs | 1 | ||||
-rw-r--r-- | src/shared/ability.rs | 11 | ||||
-rw-r--r-- | src/shared/champion.rs | 45 | ||||
-rw-r--r-- | src/shared/damage.rs | 37 | ||||
-rw-r--r-- | src/shared/projectile.rs | 3 | ||||
-rw-r--r-- | src/shared/stats.rs | 2 |
7 files changed, 170 insertions, 79 deletions
diff --git a/src/server.rs b/src/server.rs index 01dfe93..4169178 100644 --- a/src/server.rs +++ b/src/server.rs @@ -478,39 +478,46 @@ fn projectile_despawn( entity_map: Res<EntityMap>, mut commands: Commands, mut connection_manager: ResMut<ServerConnectionManager>, - mut healths: Query<&mut Health>, - player_positions: Query<&PlayerPosition>, projectiles: Query<(Entity, &mut Projectile)>, - projectile_targets: Query<(&PlayerId, &PlayerPosition, &Shape)>, + mut targets: Query<( + Entity, + &PlayerId, + &PlayerPosition, + &Shape, + &EffectiveStats, + &mut Health, + )>, ) { for (entity, projectile) in projectiles.iter() { - let (despawn, maybe_target_player): (bool, Option<PlayerId>) = (|| match &projectile.type_ { + let (despawn, hit_target): (bool, Option<Entity>) = (|| match &projectile.type_ { ProjectileType::Free(free_projectile) => { - let mut maybe_target_player = None; - let mut maybe_target_distance = None; - for (player, position, player_shape) in projectile_targets.iter() { - if *player == projectile.source_player { + let mut closest_target: Option<(Entity, f32)> = None; + for ( + target_entity, + target_player_id, + target_player_position, + target_shape, + _target_effective_stats, + _target_health, + ) in targets.iter() + { + if *target_player_id == projectile.source_player { continue; } - let distance = free_projectile.position.distance(position.0); - if distance > player_shape.radius { + let target_distance = + free_projectile.position.distance(target_player_position.0); + if target_distance > target_shape.radius { continue; } - match maybe_target_distance { - Some(old_distance) => { - if distance < old_distance { - maybe_target_player = Some(player); - maybe_target_distance = Some(distance); - } - } - None => { - maybe_target_player = Some(player); - maybe_target_distance = Some(distance); - } + let target_is_closer = closest_target + .map(|(_, closest_distance)| target_distance < closest_distance) + .unwrap_or(true); + if target_is_closer { + closest_target = Some((target_entity, target_distance)); } } - if let Some(target_player) = maybe_target_player { - (true, Some(*target_player)) + if let Some((target_entity, _)) = closest_target { + (true, Some(target_entity)) } else { ( free_projectile @@ -522,56 +529,79 @@ fn projectile_despawn( } } ProjectileType::Instant(instant_projectile) => { - (true, Some(instant_projectile.target_player)) + let Some(target_entity) = entity_map.0.get(&instant_projectile.target_player.0) + else { + return (true, None); + }; + (true, Some(*target_entity)) } ProjectileType::Targeted(targeted_projectile) => { let Some(target_entity) = entity_map.0.get(&targeted_projectile.target_player.0) else { return (true, None); }; - let Ok(target_position) = player_positions.get(*target_entity) else { + let Ok(( + target_entity, + _target_player_id, + target_player_position, + target_shape, + _target_effective_stats, + _target_health, + )) = targets.get(*target_entity) + else { return (true, None); }; - ( - targeted_projectile.position.distance(target_position.0) <= f32::EPSILON, - Some(targeted_projectile.target_player), - ) + if targeted_projectile + .position + .distance(target_player_position.0) + < target_shape.radius + { + (true, Some(target_entity)) + } else { + (false, None) + } } })(); if despawn { - if let Some(target_player) = maybe_target_player { - if let Some(target_entity) = entity_map.0.get(&target_player.0) { - if let Ok(mut health) = healths.get_mut(*target_entity) { - let damage = match projectile.type_ { - ProjectileType::Free(FreeProjectile { - position, - starting_position, - max_distance, - scale_damage: Some((min_multiplier, max_multiplier)), - .. - }) => { - let distance_traveled = - position.distance(starting_position) / max_distance; - let multiplier = - min_multiplier.lerp(max_multiplier, distance_traveled); - multiplier * projectile.damage - } - _ => projectile.damage, - }; - health.apply_damage(damage); - let _ = connection_manager - .send_message_to_target::<Channel1, HealthChanged>( - HealthChanged(HealthEvent { - target_player, - health_gained: -damage, - }), - NetworkTarget::All, - ); - } - } - } commands.entity(entity).despawn(); } + let Some(hit_entity) = hit_target else { + continue; + }; + let Ok(( + _hit_entity, + hit_player_id, + _hit_player_position, + _hit_shape, + hit_effective_stats, + mut hit_health, + )) = targets.get_mut(hit_entity) + else { + continue; + }; + let damage = match projectile.type_ { + ProjectileType::Free(FreeProjectile { + position, + starting_position, + max_distance, + scale_damage: Some((min_multiplier, max_multiplier)), + .. + }) => { + let distance_traveled = position.distance(starting_position) / max_distance; + let multiplier = min_multiplier.lerp(max_multiplier, distance_traveled); + projectile.damage.scale(multiplier) + } + _ => projectile.damage, + }; + let applied_damage = damage.apply(hit_effective_stats.0); + hit_health.apply_damage(applied_damage); + let _ = connection_manager.send_message_to_target::<Channel1, HealthChanged>( + HealthChanged(HealthEvent { + target_player: *hit_player_id, + health_gained: -applied_damage, + }), + NetworkTarget::All, + ); } } diff --git a/src/shared.rs b/src/shared.rs index a4ed8fc..172ce6b 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -15,6 +15,7 @@ pub mod area_of_effect; pub mod buffs; pub mod champion; pub mod cooldown; +pub mod damage; pub mod faction; pub mod health; pub mod health_event; diff --git a/src/shared/ability.rs b/src/shared/ability.rs index 4723e59..a9387f3 100644 --- a/src/shared/ability.rs +++ b/src/shared/ability.rs @@ -1,6 +1,7 @@ use crate::server::entity_map::*; use crate::shared::area_of_effect::*; use crate::shared::buffs::*; +use crate::shared::damage::*; use crate::shared::player::*; use crate::shared::projectile::*; use crate::shared::shape::*; @@ -24,12 +25,12 @@ pub enum TargetedAbility { #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct MeeleAttack { - pub damage: f32, + pub damage: Damage, } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct RangedAttack { - pub damage: f32, + pub damage: Damage, } impl TargetedAbility { @@ -189,7 +190,7 @@ pub enum DirectionalAbility { #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Dash { pub max_distance: f32, - pub damage: f32, + pub damage: Damage, } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] @@ -200,13 +201,13 @@ pub struct Flash { #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Pull { pub max_distance: f32, - pub damage: f32, + pub damage: Damage, } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Spear { pub max_distance: f32, - pub damage: f32, + pub damage: Damage, } // `direction: Vec2` is NOT normalized (cf. `Flash`)! diff --git a/src/shared/champion.rs b/src/shared/champion.rs index 64fc32e..c082ba3 100644 --- a/src/shared/champion.rs +++ b/src/shared/champion.rs @@ -1,4 +1,5 @@ use crate::shared::ability::*; +use crate::shared::damage::*; use crate::shared::shape::*; use crate::shared::stats::*; use crate::shared::*; @@ -43,30 +44,40 @@ impl Champion { attack_speed: 0.75, max_health: 150., movement_speed: 75., + armor: 100., + magic_resist: 100., }), Champion::Ranged => BaseStats(Stats { attack_range: 50., attack_speed: 1.5, max_health: 100., movement_speed: 85., + armor: 50., + magic_resist: 50., }), Champion::Tower => BaseStats(Stats { attack_range: 100., attack_speed: 4.5, max_health: 500., movement_speed: 0., + armor: 0., + magic_resist: 0., }), Champion::Minion => BaseStats(Stats { attack_range: 30., attack_speed: 1., max_health: 50., movement_speed: 60., + armor: 0., + magic_resist: 0., }), Champion::Nexus => BaseStats(Stats { attack_range: 0., attack_speed: 0., max_health: 2000., movement_speed: 0., + armor: 0., + magic_resist: 0., }), } } @@ -80,27 +91,29 @@ impl Champion { })), AbilitySlot::W => Ability::Directional(DirectionalAbility::Dash(Dash { max_distance: 60., - damage: 15., + damage: Damage::Physical(30.), })), AbilitySlot::E => Ability::Directional(DirectionalAbility::Pull(Pull { max_distance: 60., - damage: 10., + damage: Damage::Physical(20.), + })), + AbilitySlot::R => Ability::Targeted(TargetedAbility::MeeleAttack(MeeleAttack { + damage: Damage::True(65.), })), - AbilitySlot::R => { - Ability::Targeted(TargetedAbility::MeeleAttack(MeeleAttack { damage: 45. })) - } AbilitySlot::F => { Ability::Directional(DirectionalAbility::Flash(Flash { max_distance: 100. })) } AbilitySlot::G => { Ability::Activated(ActivatedAbility::Speed(Speed { duration: 2.5 })) } - _ => Ability::Targeted(TargetedAbility::MeeleAttack(MeeleAttack { damage: 5. })), + _ => Ability::Targeted(TargetedAbility::MeeleAttack(MeeleAttack { + damage: Damage::Physical(10.), + })), }, Champion::Ranged => match ability_slot { AbilitySlot::E => Ability::Directional(DirectionalAbility::Spear(Spear { max_distance: 250., - damage: 25., + damage: Damage::Physical(50.), })), AbilitySlot::R => { Ability::Activated(ActivatedAbility::Focus(Focus { duration: 5. })) @@ -111,18 +124,24 @@ impl Champion { AbilitySlot::G => { Ability::Activated(ActivatedAbility::Speed(Speed { duration: 2.5 })) } - _ => Ability::Targeted(TargetedAbility::RangedAttack(RangedAttack { damage: 8. })), + _ => Ability::Targeted(TargetedAbility::RangedAttack(RangedAttack { + damage: Damage::Physical(15.), + })), }, Champion::Tower => match ability_slot { - _ => { - Ability::Targeted(TargetedAbility::RangedAttack(RangedAttack { damage: 100. })) - } + _ => Ability::Targeted(TargetedAbility::RangedAttack(RangedAttack { + damage: Damage::Physical(100.), + })), }, Champion::Minion => match ability_slot { - _ => Ability::Targeted(TargetedAbility::RangedAttack(RangedAttack { damage: 2. })), + _ => Ability::Targeted(TargetedAbility::RangedAttack(RangedAttack { + damage: Damage::Physical(5.), + })), }, Champion::Nexus => match ability_slot { - _ => Ability::Targeted(TargetedAbility::MeeleAttack(MeeleAttack { damage: 0. })), + _ => Ability::Targeted(TargetedAbility::MeeleAttack(MeeleAttack { + damage: Damage::Physical(0.), + })), }, } } diff --git a/src/shared/damage.rs b/src/shared/damage.rs new file mode 100644 index 0000000..c336d87 --- /dev/null +++ b/src/shared/damage.rs @@ -0,0 +1,37 @@ +use crate::shared::stats::*; +use crate::shared::*; + +#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] +pub enum Damage { + Magical(f32), + Physical(f32), + True(f32), +} + +impl Damage { + pub fn apply(self, stats: Stats) -> f32 { + match self { + Damage::Magical(magical_damage) => { + magical_damage * damage_multiplier(stats.magic_resist) + } + Damage::Physical(physical_damage) => physical_damage * damage_multiplier(stats.armor), + Damage::True(true_damage) => true_damage, + } + } + + pub fn scale(self, factor: f32) -> Self { + match self { + Damage::Magical(magical_damage) => Damage::Magical(magical_damage * factor), + Damage::Physical(physical_damage) => Damage::Physical(physical_damage * factor), + Damage::True(true_damage) => Damage::True(true_damage * factor), + } + } +} + +fn damage_multiplier(resistance: f32) -> f32 { + if resistance >= 0. { + 100. / (100. + resistance) + } else { + 2. - 100. / (100. - resistance) + } +} diff --git a/src/shared/projectile.rs b/src/shared/projectile.rs index f9fb592..5da0526 100644 --- a/src/shared/projectile.rs +++ b/src/shared/projectile.rs @@ -1,3 +1,4 @@ +use crate::shared::damage::*; use crate::shared::player::*; use crate::shared::*; @@ -24,7 +25,7 @@ impl ProjectileBundle { pub struct Projectile { pub type_: ProjectileType, pub source_player: PlayerId, - pub damage: f32, + pub damage: Damage, } #[derive(Component, Message, Serialize, Deserialize, Clone, Debug, PartialEq)] diff --git a/src/shared/stats.rs b/src/shared/stats.rs index 239405e..98c26aa 100644 --- a/src/shared/stats.rs +++ b/src/shared/stats.rs @@ -6,6 +6,8 @@ pub struct Stats { pub attack_speed: f32, pub max_health: f32, pub movement_speed: f32, + pub armor: f32, + pub magic_resist: f32, } #[derive(Component, Message, Clone, Copy, Serialize, Deserialize, PartialEq, Debug)] |