Transitioned damage and healing from u32/i32s to enums.

This commit is contained in:
Sam 2020-10-17 17:42:43 -05:00
parent 844e6f2b60
commit a7e3e55a12
26 changed files with 331 additions and 323 deletions

161
common/src/combat.rs Normal file
View File

@ -0,0 +1,161 @@
use crate::{
comp::{HealthChange, HealthSource, Loadout},
sync::Uid,
};
use serde::{Deserialize, Serialize};
pub const BLOCK_EFFICIENCY: f32 = 0.9;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Damages {
pub enemy: Option<Damage>,
pub group: Option<Damage>,
}
impl Damages {
pub fn new(enemy: Option<Damage>, group: Option<Damage>) -> Self { Damages { enemy, group } }
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Damage {
Melee(f32),
Healing(f32),
Projectile(f32),
Explosion(f32),
Falling(f32),
Shockwave(f32),
Energy(f32),
}
impl Damage {
pub fn modify_damage(
self,
block: bool,
loadout: Option<&Loadout>,
uid: Option<Uid>,
) -> HealthChange {
match self {
Damage::Melee(damage) => {
let mut damage = damage;
// Critical hit
let mut critdamage = 0.0;
if rand::random() {
critdamage = damage * 0.3;
}
// Block
if block {
damage *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = if let Some(loadout) = loadout {
loadout.get_damage_reduction()
} else {
0.0
};
damage *= 1.0 - damage_reduction;
// Critical damage applies after armor for melee
if (damage_reduction - 1.0).abs() > f32::EPSILON {
damage += critdamage;
}
HealthChange {
amount: -damage as i32,
cause: HealthSource::Attack { by: uid.unwrap() },
}
},
Damage::Projectile(damage) => {
let mut damage = damage;
// Critical hit
if rand::random() {
damage *= 1.2;
}
// Block
if block {
damage *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = if let Some(loadout) = loadout {
loadout.get_damage_reduction()
} else {
0.0
};
damage *= 1.0 - damage_reduction;
HealthChange {
amount: -damage as i32,
cause: HealthSource::Projectile { owner: uid },
}
},
Damage::Explosion(damage) => {
let mut damage = damage;
// Block
if block {
damage *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = if let Some(loadout) = loadout {
loadout.get_damage_reduction()
} else {
0.0
};
damage *= 1.0 - damage_reduction;
HealthChange {
amount: -damage as i32,
cause: HealthSource::Explosion { owner: uid },
}
},
Damage::Shockwave(damage) => {
let mut damage = damage;
// Armor
let damage_reduction = if let Some(loadout) = loadout {
loadout.get_damage_reduction()
} else {
0.0
};
damage *= 1.0 - damage_reduction;
HealthChange {
amount: -damage as i32,
cause: HealthSource::Attack { by: uid.unwrap() },
}
},
Damage::Energy(damage) => {
let mut damage = damage;
// Armor
let damage_reduction = if let Some(loadout) = loadout {
loadout.get_damage_reduction()
} else {
0.0
};
damage *= 1.0 - damage_reduction;
HealthChange {
amount: -damage as i32,
cause: HealthSource::Energy { owner: uid },
}
},
Damage::Healing(heal) => HealthChange {
amount: heal as i32,
cause: HealthSource::Healing { by: uid },
},
Damage::Falling(damage) => {
let mut damage = damage;
// Armor
let damage_reduction = if let Some(loadout) = loadout {
loadout.get_damage_reduction()
} else {
0.0
};
if (damage_reduction - 1.0).abs() < f32::EPSILON {
damage = 0.0;
}
HealthChange {
amount: -damage as i32,
cause: HealthSource::World,
}
},
}
}
}

View File

@ -1,4 +1,4 @@
use crate::sync::Uid;
use crate::{sync::Uid, Damages};
use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage};
use specs_idvs::IdvStorage;
@ -8,8 +8,7 @@ use std::time::Duration;
pub struct Properties {
pub angle: f32,
pub speed: f32,
pub damage: u32,
pub heal: u32,
pub damages: Damages,
pub lifesteal_eff: f32,
pub energy_regen: u32,
pub energy_cost: u32,

View File

@ -3,6 +3,7 @@ use crate::{
event::{LocalEvent, ServerEvent},
states::*,
sys::character_behavior::JoinData,
Damages,
};
use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage, VecStorage};
@ -152,8 +153,7 @@ impl Component for CharacterState {
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Attacking {
pub base_damage: u32,
pub base_heal: u32,
pub damages: Damages,
pub range: f32,
pub max_angle: f32,
pub applied: bool,

View File

@ -1,79 +0,0 @@
use crate::comp::Loadout;
use serde::{Deserialize, Serialize};
pub const BLOCK_EFFICIENCY: f32 = 0.9;
pub struct Damage {
pub healthchange: f32,
pub source: DamageSource,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DamageSource {
Melee,
Healing,
Projectile,
Explosion,
Falling,
Shockwave,
Energy,
}
impl Damage {
pub fn modify_damage(&mut self, block: bool, loadout: &Loadout) {
match self.source {
DamageSource::Melee => {
// Critical hit
let mut critdamage = 0.0;
if rand::random() {
critdamage = self.healthchange * 0.3;
}
// Block
if block {
self.healthchange *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
// Critical damage applies after armor for melee
if (damage_reduction - 1.0).abs() > f32::EPSILON {
self.healthchange += critdamage;
}
},
DamageSource::Projectile => {
// Critical hit
if rand::random() {
self.healthchange *= 1.2;
}
// Block
if block {
self.healthchange *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
},
DamageSource::Explosion => {
// Block
if block {
self.healthchange *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
},
DamageSource::Shockwave => {
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
},
DamageSource::Energy => {
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
},
_ => {},
}
}
}

View File

@ -4,7 +4,7 @@
use crate::{
comp::{body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile},
states::combo_melee,
Explosion,
Damage, Damages, Explosion,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -298,7 +298,10 @@ impl Tool {
projectile: Projectile {
hit_solid: vec![projectile::Effect::Stick],
hit_entity: vec![
projectile::Effect::Damage((-40.0 * self.base_power()) as i32),
projectile::Effect::Damages(Damages::new(
Some(Damage::Projectile(40.0 * self.base_power())),
None,
)),
projectile::Effect::Knockback(10.0),
projectile::Effect::RewardEnergy(50),
projectile::Effect::Vanish,
@ -338,7 +341,10 @@ impl Tool {
projectile: Projectile {
hit_solid: vec![projectile::Effect::Stick],
hit_entity: vec![
projectile::Effect::Damage((-40.0 * self.base_power()) as i32),
projectile::Effect::Damages(Damages::new(
Some(Damage::Projectile(40.0 * self.base_power())),
None,
)),
projectile::Effect::Knockback(10.0),
projectile::Effect::RewardEnergy(50),
projectile::Effect::Vanish,

View File

@ -7,7 +7,6 @@ pub mod buff;
mod character_state;
pub mod chat;
mod controller;
mod damage;
mod energy;
pub mod group;
mod inputs;
@ -44,7 +43,6 @@ pub use controller::{
Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, Input,
InventoryManip, MountState, Mounting,
};
pub use damage::{Damage, DamageSource};
pub use energy::{Energy, EnergySource};
pub use group::Group;
pub use inputs::CanBuild;

View File

@ -1,4 +1,4 @@
use crate::{sync::Uid, Explosion};
use crate::{sync::Uid, Damages, Explosion};
use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage};
use specs_idvs::IdvStorage;
@ -6,7 +6,7 @@ use std::time::Duration;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Effect {
Damage(i32),
Damages(Damages),
Knockback(f32),
RewardEnergy(u32),
Explode(Explosion),

View File

@ -1,4 +1,4 @@
use crate::sync::Uid;
use crate::{sync::Uid, Damages};
use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage};
use specs_idvs::IdvStorage;
@ -9,7 +9,7 @@ pub struct Properties {
pub angle: f32,
pub vertical_angle: f32,
pub speed: f32,
pub damage: u32,
pub damages: Damages,
pub knockback: f32,
pub requires_ground: bool,
pub duration: Duration,

View File

@ -22,6 +22,7 @@ pub mod astar;
pub mod character;
pub mod clock;
pub mod cmd;
pub mod combat;
pub mod comp;
pub mod effect;
pub mod event;
@ -51,5 +52,6 @@ pub mod util;
pub mod vol;
pub mod volumes;
pub use combat::{Damage, Damages};
pub use explosion::Explosion;
pub use loadout_builder::LoadoutBuilder;

View File

@ -9,5 +9,5 @@ pub struct SysMetrics {
pub stats_ns: AtomicI64,
pub phys_ns: AtomicI64,
pub projectile_ns: AtomicI64,
pub combat_ns: AtomicI64,
pub melee_ns: AtomicI64,
}

View File

@ -4,6 +4,7 @@ use crate::{
states::utils::*,
sync::Uid,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, Damages,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -108,10 +109,12 @@ impl CharacterBehavior for Data {
if ability_key_is_pressed(data, self.static_data.ability_key)
&& (self.static_data.energy_drain == 0 || update.energy.current() > 0)
{
let damage =
(self.static_data.base_dps as f32 / self.static_data.tick_rate) as u32;
let heal =
(self.static_data.base_hps as f32 / self.static_data.tick_rate) as u32;
let damage = Damage::Energy(
self.static_data.base_dps as f32 / self.static_data.tick_rate,
);
let heal = Damage::Healing(
self.static_data.base_hps as f32 / self.static_data.tick_rate,
);
let energy_regen =
(self.static_data.energy_regen as f32 / self.static_data.tick_rate) as u32;
let energy_cost =
@ -121,8 +124,7 @@ impl CharacterBehavior for Data {
let properties = beam::Properties {
angle: self.static_data.max_angle.to_radians(),
speed,
damage,
heal,
damages: Damages::new(Some(damage), Some(heal)),
lifesteal_eff: self.static_data.lifesteal_eff,
energy_regen,
energy_cost,

View File

@ -2,6 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, Damages,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -79,8 +80,10 @@ impl CharacterBehavior for Data {
// Hit attempt
data.updater.insert(data.entity, Attacking {
base_damage: self.static_data.base_damage,
base_heal: 0,
damages: Damages::new(
Some(Damage::Melee(self.static_data.base_damage as f32)),
None,
),
range: self.static_data.range,
max_angle: 180_f32.to_radians(),
applied: false,

View File

@ -2,6 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::utils::{StageSection, *},
sys::character_behavior::*,
Damage, Damages,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -115,17 +116,16 @@ impl CharacterBehavior for Data {
},
StageSection::Swing => {
if !self.exhausted {
let damage = self.static_data.initial_damage
+ ((self.static_data.max_damage - self.static_data.initial_damage) as f32
* self.charge_amount) as u32;
let damage = self.static_data.initial_damage as f32
+ (self.static_data.max_damage - self.static_data.initial_damage) as f32
* self.charge_amount;
let knockback = self.static_data.initial_knockback
+ (self.static_data.max_knockback - self.static_data.initial_knockback)
* self.charge_amount;
// Hit attempt
data.updater.insert(data.entity, Attacking {
base_damage: damage as u32,
base_heal: 0,
damages: Damages::new(Some(Damage::Melee(damage)), None),
range: self.static_data.range,
max_angle: self.static_data.max_angle.to_radians(),
applied: false,

View File

@ -6,6 +6,7 @@ use crate::{
event::ServerEvent,
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, Damages,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -97,7 +98,10 @@ impl CharacterBehavior for Data {
let mut projectile = Projectile {
hit_solid: vec![projectile::Effect::Stick],
hit_entity: vec![
projectile::Effect::Damage(-damage as i32),
projectile::Effect::Damages(Damages::new(
Some(Damage::Projectile(damage)),
None,
)),
projectile::Effect::Knockback(knockback),
projectile::Effect::Vanish,
],

View File

@ -2,6 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, Damages,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -125,13 +126,13 @@ impl CharacterBehavior for Data {
});
// Hit attempt
let damage = self.static_data.stage_data[stage_index].max_damage.min(
self.static_data.stage_data[stage_index].base_damage
+ self.combo / self.static_data.num_stages
* self.static_data.stage_data[stage_index].damage_increase,
);
data.updater.insert(data.entity, Attacking {
base_damage: self.static_data.stage_data[stage_index].max_damage.min(
self.static_data.stage_data[stage_index].base_damage
+ self.combo / self.static_data.num_stages
* self.static_data.stage_data[stage_index].damage_increase,
),
base_heal: 0,
damages: Damages::new(Some(Damage::Melee(damage as f32)), None),
range: self.static_data.stage_data[stage_index].range,
max_angle: self.static_data.stage_data[stage_index].angle.to_radians(),
applied: false,

View File

@ -2,6 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, Damages,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -121,8 +122,7 @@ impl CharacterBehavior for Data {
* charge_frac
+ self.static_data.base_knockback;
data.updater.insert(data.entity, Attacking {
base_damage: damage as u32,
base_heal: 0,
damages: Damages::new(Some(Damage::Melee(damage)), None),
range: self.static_data.range,
max_angle: self.static_data.angle.to_radians(),
applied: false,

View File

@ -2,6 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, StateUpdate},
states::utils::{StageSection, *},
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, Damages,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -141,8 +142,10 @@ impl CharacterBehavior for Data {
if !self.exhausted {
// Hit attempt, when animation plays
data.updater.insert(data.entity, Attacking {
base_damage: self.static_data.base_damage,
base_heal: 0,
damages: Damages::new(
Some(Damage::Melee(self.static_data.base_damage as f32)),
None,
),
range: self.static_data.range,
max_angle: self.static_data.max_angle.to_radians(),
applied: false,

View File

@ -3,6 +3,7 @@ use crate::{
event::ServerEvent,
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, Damages,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -70,7 +71,10 @@ impl CharacterBehavior for Data {
vertical_angle: self.static_data.shockwave_vertical_angle,
speed: self.static_data.shockwave_speed,
duration: self.static_data.shockwave_duration,
damage: self.static_data.damage,
damages: Damages::new(
Some(Damage::Shockwave(self.static_data.damage as f32)),
None,
),
knockback: self.static_data.knockback,
requires_ground: self.static_data.requires_ground,
owner: Some(*data.uid),

View File

@ -5,6 +5,7 @@ use crate::{
character_behavior::{CharacterBehavior, JoinData},
phys::GRAVITY,
},
Damage, Damages,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -110,8 +111,10 @@ impl CharacterBehavior for Data {
});
// Hit attempt
data.updater.insert(data.entity, Attacking {
base_damage: self.static_data.base_damage,
base_heal: 0,
damages: Damages::new(
Some(Damage::Melee(self.static_data.base_damage as f32)),
None,
),
range: self.static_data.range,
max_angle: 180_f32.to_radians(),
applied: false,

View File

@ -1,11 +1,12 @@
use crate::{
comp::{
group, Beam, BeamSegment, Body, CharacterState, Damage, DamageSource, Energy, EnergySource,
HealthChange, HealthSource, Last, Loadout, Ori, Pos, Scale, Stats,
group, Beam, BeamSegment, Body, CharacterState, Energy, EnergySource, HealthChange,
HealthSource, Last, Loadout, Ori, Pos, Scale, Stats,
},
event::{EventBus, ServerEvent},
state::{DeltaTime, Time},
sync::{Uid, UidAllocator},
Damage,
};
use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage};
use std::time::Duration;
@ -166,54 +167,31 @@ impl<'a> System<'a> for Sys {
if Some(*uid_b) == beam_segment.owner {
continue;
}
// Don't heal if outside group
// Don't damage in the same group
let is_damage = !same_group && (beam_segment.damage > 0);
let is_heal = same_group && (beam_segment.heal > 0);
if !is_heal && !is_damage {
let damage = if !same_group && beam_segment.damages.enemy.is_some() {
beam_segment.damages.enemy.unwrap()
} else if same_group && beam_segment.damages.group.is_some() {
beam_segment.damages.group.unwrap()
} else {
continue;
}
// Weapon gives base damage
let source = if is_heal {
DamageSource::Healing
} else {
DamageSource::Energy
};
let healthchange = if is_heal {
beam_segment.heal as f32
} else {
-(beam_segment.damage as f32)
};
let mut damage = Damage {
healthchange,
source,
};
let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false)
// TODO: investigate whether this calculation is proper for beams
&& ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0;
if let Some(loadout) = loadouts.get(b) {
damage.modify_damage(block, loadout);
}
let change = damage.modify_damage(block, loadouts.get(b), beam_segment.owner);
if is_damage {
if let Damage::Energy(_) = damage {
server_emitter.emit(ServerEvent::Damage {
uid: *uid_b,
change: HealthChange {
amount: damage.healthchange as i32,
cause: HealthSource::Energy {
owner: beam_segment.owner,
},
},
change,
});
if beam_segment.lifesteal_eff > 0.0 {
server_emitter.emit(ServerEvent::Damage {
uid: beam_segment.owner.unwrap_or(*uid),
change: HealthChange {
amount: (-damage.healthchange * beam_segment.lifesteal_eff)
amount: (-change.amount as f32 * beam_segment.lifesteal_eff)
as i32,
cause: HealthSource::Healing {
by: beam_segment.owner,
@ -228,7 +206,7 @@ impl<'a> System<'a> for Sys {
);
}
}
if is_heal {
if let Damage::Healing(_) = damage {
if let Some(energy_mut) = beam_owner.and_then(|o| energies.get_mut(o)) {
if energy_mut
.try_change_by(
@ -239,12 +217,7 @@ impl<'a> System<'a> for Sys {
{
server_emitter.emit(ServerEvent::Damage {
uid: *uid_b,
change: HealthChange {
amount: damage.healthchange as i32,
cause: HealthSource::Healing {
by: beam_segment.owner,
},
},
change,
});
}
}

View File

@ -1,8 +1,5 @@
use crate::{
comp::{
buff, group, Attacking, Body, CharacterState, Damage, DamageSource, HealthChange,
HealthSource, Loadout, Ori, Pos, Scale, Stats,
},
comp::{buff, group, Attacking, Body, CharacterState, Loadout, Ori, Pos, Scale, Stats},
event::{EventBus, LocalEvent, ServerEvent},
metrics::SysMetrics,
span,
@ -59,7 +56,7 @@ impl<'a> System<'a> for Sys {
): Self::SystemData,
) {
let start_time = std::time::Instant::now();
span!(_guard, "run", "combat::Sys::run");
span!(_guard, "run", "melee::Sys::run");
let mut server_emitter = server_bus.emitter();
let mut _local_emitter = local_bus.emitter();
// Attacks
@ -113,56 +110,36 @@ impl<'a> System<'a> for Sys {
.get(entity)
.map(|group_a| Some(group_a) == groups.get(b))
.unwrap_or(false);
// Don't heal if outside group
// Don't damage in the same group
let is_damage = !same_group && (attack.base_damage > 0);
let is_heal = same_group && (attack.base_heal > 0);
if !is_heal && !is_damage {
continue;
}
// Weapon gives base damage
let (source, healthchange) = if is_heal {
(DamageSource::Healing, attack.base_heal as f32)
let damage = if !same_group && attack.damages.enemy.is_some() {
attack.damages.enemy.unwrap()
} else if same_group && attack.damages.group.is_some() {
attack.damages.group.unwrap()
} else {
(DamageSource::Melee, -(attack.base_damage as f32))
};
let mut damage = Damage {
healthchange,
source,
continue;
};
let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false)
&& ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0;
if let Some(loadout) = loadouts.get(b) {
damage.modify_damage(block, loadout);
}
let change = damage.modify_damage(block, loadouts.get(b), Some(*uid));
if damage.healthchange != 0.0 {
let cause = if is_heal {
HealthSource::Healing { by: Some(*uid) }
} else {
HealthSource::Attack { by: *uid }
};
if change.amount != 0 {
server_emitter.emit(ServerEvent::Damage {
uid: *uid_b,
change: HealthChange {
amount: damage.healthchange as i32,
cause,
},
change,
});
// Apply bleeding buff on melee hits with 10% chance
// TODO: Don't have buff uniformly applied on all melee attacks
if thread_rng().gen::<f32>() < 0.1 {
if change.amount < 0 && thread_rng().gen::<f32>() < 0.1 {
use buff::*;
server_emitter.emit(ServerEvent::Buff {
entity: b,
buff_change: BuffChange::Add(Buff::new(
BuffKind::Bleeding,
BuffData {
strength: attack.base_damage as f32 / 10.0,
strength: -change.amount as f32 / 10.0,
duration: Some(Duration::from_secs(10)),
},
vec![BuffCategory::Physical],
@ -172,7 +149,8 @@ impl<'a> System<'a> for Sys {
}
attack.hit_count += 1;
}
if attack.knockback != 0.0 && damage.healthchange != 0.0 {
if attack.knockback != 0.0 && change.amount != 0 {
let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0));
server_emitter.emit(ServerEvent::Knockback {
entity: b,
@ -183,7 +161,7 @@ impl<'a> System<'a> for Sys {
}
}
}
sys_metrics.combat_ns.store(
sys_metrics.melee_ns.store(
start_time.elapsed().as_nanos() as i64,
std::sync::atomic::Ordering::Relaxed,
);

View File

@ -2,8 +2,8 @@ pub mod agent;
mod beam;
mod buff;
pub mod character_behavior;
pub mod combat;
pub mod controller;
pub mod melee;
mod mount;
pub mod phys;
mod projectile;
@ -15,7 +15,7 @@ use specs::DispatcherBuilder;
// System names
pub const CHARACTER_BEHAVIOR_SYS: &str = "character_behavior_sys";
pub const COMBAT_SYS: &str = "combat_sys";
pub const MELEE_SYS: &str = "melee_sys";
pub const AGENT_SYS: &str = "agent_sys";
pub const BEAM_SYS: &str = "beam_sys";
pub const CONTROLLER_SYS: &str = "controller_sys";
@ -39,5 +39,5 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch_builder.add(projectile::Sys, PROJECTILE_SYS, &[PHYS_SYS]);
dispatch_builder.add(shockwave::Sys, SHOCKWAVE_SYS, &[PHYS_SYS]);
dispatch_builder.add(beam::Sys, BEAM_SYS, &[PHYS_SYS]);
dispatch_builder.add(combat::Sys, COMBAT_SYS, &[PROJECTILE_SYS]);
dispatch_builder.add(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]);
}

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
projectile, Damage, DamageSource, Energy, EnergySource, Group, HealthChange, HealthSource,
Loadout, Ori, PhysicsState, Pos, Projectile, Vel,
projectile, Energy, EnergySource, Group, HealthSource, Loadout, Ori, PhysicsState, Pos,
Projectile, Vel,
},
event::{EventBus, LocalEvent, ServerEvent},
metrics::SysMetrics,
@ -73,20 +73,21 @@ impl<'a> System<'a> for Sys {
{
// Hit entity
for other in physics.touch_entities.iter().copied() {
let same_group = projectile
.owner
// Note: somewhat inefficient since we do the lookup for every touching
// entity, but if we pull this out of the loop we would want to do it only
// if there is at least one touching entity
.and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into()))
.and_then(|e| groups.get(e))
.map_or(false, |owner_group|
Some(owner_group) == uid_allocator
.retrieve_entity_internal(other.into())
.and_then(|e| groups.get(e))
);
if projectile.ignore_group
// Skip if in the same group
&& projectile
.owner
// Note: somewhat inefficient since we do the lookup for every touching
// entity, but if we pull this out of the loop we would want to do it only
// if there is at least one touching entity
.and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into()))
.and_then(|e| groups.get(e))
.map_or(false, |owner_group|
Some(owner_group) == uid_allocator
.retrieve_entity_internal(other.into())
.and_then(|e| groups.get(e))
)
&& same_group
{
continue;
}
@ -97,40 +98,25 @@ impl<'a> System<'a> for Sys {
for effect in projectile.hit_entity.drain(..) {
match effect {
projectile::Effect::Damage(healthchange) => {
let owner_uid = projectile.owner.unwrap();
let mut damage = Damage {
healthchange: healthchange as f32,
source: DamageSource::Projectile,
};
let other_entity = uid_allocator.retrieve_entity_internal(other.into());
if let Some(loadout) = other_entity.and_then(|e| loadouts.get(e)) {
damage.modify_damage(false, loadout);
projectile::Effect::Damages(damages) => {
if Some(other) == projectile.owner {
continue;
}
let damage = if !same_group && damages.enemy.is_some() {
damages.enemy.unwrap()
} else if same_group && damages.group.is_some() {
damages.group.unwrap()
} else {
continue;
};
let other_entity_loadout = uid_allocator
.retrieve_entity_internal(other.into())
.and_then(|e| loadouts.get(e));
let change =
damage.modify_damage(false, other_entity_loadout, projectile.owner);
if other != owner_uid {
if damage.healthchange < 0.0 {
server_emitter.emit(ServerEvent::Damage {
uid: other,
change: HealthChange {
amount: damage.healthchange as i32,
cause: HealthSource::Projectile {
owner: Some(owner_uid),
},
},
});
} else if damage.healthchange > 0.0 {
server_emitter.emit(ServerEvent::Damage {
uid: other,
change: HealthChange {
amount: damage.healthchange as i32,
cause: HealthSource::Healing {
by: Some(owner_uid),
},
},
});
}
if change.amount != 0 {
server_emitter.emit(ServerEvent::Damage { uid: other, change });
}
},
projectile::Effect::Knockback(knockback) => {

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
group, Body, CharacterState, Damage, DamageSource, HealthChange, HealthSource, Last,
Loadout, Ori, PhysicsState, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
group, Body, CharacterState, HealthSource, Last, Loadout, Ori, PhysicsState, Pos, Scale,
Shockwave, ShockwaveHitEntities, Stats,
},
event::{EventBus, LocalEvent, ServerEvent},
state::{DeltaTime, Time},
@ -189,42 +189,31 @@ impl<'a> System<'a> for Sys {
})
}
&& (pos_b_ground - pos.0).angle_between(pos_b.0 - pos.0) < max_angle
&& (!shockwave.requires_ground || physics_state_b.on_ground)
&& !same_group;
&& (!shockwave.requires_ground || physics_state_b.on_ground);
if hit {
let mut damage = Damage {
healthchange: -(shockwave.damage as f32),
source: DamageSource::Shockwave,
let damage = if !same_group && shockwave.damages.enemy.is_some() {
shockwave.damages.enemy.unwrap()
} else if same_group && shockwave.damages.group.is_some() {
shockwave.damages.group.unwrap()
} else {
continue;
};
let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false)
&& ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0;
if let Some(loadout) = loadouts.get(b) {
damage.modify_damage(block, loadout);
}
let owner_uid = shockwave.owner.unwrap_or(*uid);
let change = damage.modify_damage(block, loadouts.get(b), Some(owner_uid));
if damage.healthchange != 0.0 {
let cause = if damage.healthchange < 0.0 {
HealthSource::Attack {
by: shockwave.owner.unwrap_or(*uid),
}
} else {
HealthSource::Healing {
by: Some(shockwave.owner.unwrap_or(*uid)),
}
};
if change.amount != 0 {
server_emitter.emit(ServerEvent::Damage {
uid: *uid_b,
change: HealthChange {
amount: damage.healthchange as i32,
cause,
},
change,
});
shockwave_hit_list.hit_entities.push(*uid_b);
}
if shockwave.knockback != 0.0 && damage.healthchange != 0.0 {
if shockwave.knockback != 0.0 {
let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0));
let impulse = if shockwave.knockback < 0.0 {
shockwave.knockback

View File

@ -8,18 +8,17 @@ use common::{
comp::{
self, buff,
chat::{KillSource, KillType},
object, Alignment, Body, Damage, DamageSource, Group, HealthChange, HealthSource, Item,
Player, Pos, Stats,
object, Alignment, Body, Group, HealthChange, HealthSource, Item, Player, Pos, Stats,
},
lottery::Lottery,
msg::{PlayerListUpdate, ServerGeneral},
outcome::Outcome,
state::BlockChange,
sync::{Uid, UidAllocator, WorldSyncExt},
sys::combat::BLOCK_ANGLE,
sys::melee::BLOCK_ANGLE,
terrain::{Block, TerrainGrid},
vol::ReadVol,
Explosion,
Damage, Explosion,
};
use comp::item::Reagent;
use rand::prelude::*;
@ -456,17 +455,10 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
if vel.z <= -30.0 {
if let Some(stats) = state.ecs().write_storage::<comp::Stats>().get_mut(entity) {
let falldmg = (vel.z.powi(2) / 20.0 - 40.0) * 10.0;
let mut damage = Damage {
healthchange: -falldmg,
source: DamageSource::Falling,
};
if let Some(loadout) = state.ecs().read_storage::<comp::Loadout>().get(entity) {
damage.modify_damage(false, loadout);
}
stats.health.change_by(comp::HealthChange {
amount: damage.healthchange as i32,
cause: comp::HealthSource::World,
});
let damage = Damage::Falling(falldmg);
let loadouts = state.ecs().read_storage::<comp::Loadout>();
let change = damage.modify_damage(false, loadouts.get(entity), None);
stats.health.change_by(change);
}
}
}
@ -576,43 +568,26 @@ pub fn handle_explosion(
continue;
}
// Weapon gives base damage
let source = if is_heal {
DamageSource::Healing
} else {
DamageSource::Explosion
};
let strength = 1.0 - distance_squared / explosion.radius.powi(2);
let healthchange = if is_heal {
explosion.min_heal as f32
+ (explosion.max_heal - explosion.min_heal) as f32 * strength
let damage = if is_heal {
Damage::Healing(
explosion.min_heal as f32
+ (explosion.max_heal - explosion.min_heal) as f32 * strength,
)
} else {
-(explosion.min_damage as f32
+ (explosion.max_damage - explosion.min_damage) as f32 * strength)
};
let mut damage = Damage {
healthchange,
source,
Damage::Explosion(
explosion.min_damage as f32
+ (explosion.max_damage - explosion.min_damage) as f32 * strength,
)
};
let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false)
&& ori_b.0.angle_between(pos - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0;
if let Some(loadout) = loadout_b {
damage.modify_damage(block, loadout);
}
let change = damage.modify_damage(block, loadout_b, owner);
if damage.healthchange != 0.0 {
let cause = if is_heal {
HealthSource::Healing { by: owner }
} else {
HealthSource::Explosion { owner }
};
stats_b.health.change_by(HealthChange {
amount: damage.healthchange as i32,
cause,
});
if change.amount != 0 {
stats_b.health.change_by(change);
if let Some(owner) = owner_entity {
if let Some(energy) = ecs.write_storage::<comp::Energy>().get_mut(owner) {
energy

View File

@ -717,7 +717,7 @@ impl Server {
let stats_ns = res.stats_ns.load(Ordering::Relaxed);
let phys_ns = res.phys_ns.load(Ordering::Relaxed);
let projectile_ns = res.projectile_ns.load(Ordering::Relaxed);
let combat_ns = res.combat_ns.load(Ordering::Relaxed);
let melee_ns = res.melee_ns.load(Ordering::Relaxed);
c.with_label_values(&[common::sys::AGENT_SYS])
.inc_by(agent_ns);
@ -733,8 +733,8 @@ impl Server {
.inc_by(phys_ns);
c.with_label_values(&[common::sys::PROJECTILE_SYS])
.inc_by(projectile_ns);
c.with_label_values(&[common::sys::COMBAT_SYS])
.inc_by(combat_ns);
c.with_label_values(&[common::sys::MELEE_SYS])
.inc_by(melee_ns);
const NANOSEC_PER_SEC: f64 = Duration::from_secs(1).as_nanos() as f64;
let h = &self.state_tick_metrics.state_tick_time_hist;
@ -752,8 +752,8 @@ impl Server {
.observe(phys_ns as f64 / NANOSEC_PER_SEC);
h.with_label_values(&[common::sys::PROJECTILE_SYS])
.observe(projectile_ns as f64 / NANOSEC_PER_SEC);
h.with_label_values(&[common::sys::COMBAT_SYS])
.observe(combat_ns as f64 / NANOSEC_PER_SEC);
h.with_label_values(&[common::sys::MELEE_SYS])
.observe(melee_ns as f64 / NANOSEC_PER_SEC);
}
// Report other info