aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-24 14:32:01 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-24 14:34:41 +0100
commitf7428e317d282856d05ffc37b6ba474c5e9973ce (patch)
treecf016b07c1145ad4b62c210eb739085c0639f407
parent1751960338fc7a08fec438a9b53c78118a35063a (diff)
feat: damage types
-rw-r--r--src/server.rs150
-rw-r--r--src/shared.rs1
-rw-r--r--src/shared/ability.rs11
-rw-r--r--src/shared/champion.rs45
-rw-r--r--src/shared/damage.rs37
-rw-r--r--src/shared/projectile.rs3
-rw-r--r--src/shared/stats.rs2
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)]