Removed Damages struct. Added GroupTarget enum. Changed RadiusEffect to use Effect instead of Daamges. Added Damage variant to Effect Eenum.

This commit is contained in:
Sam 2020-11-03 20:01:12 -06:00
parent d38f1d319c
commit bda7fefdc0
25 changed files with 258 additions and 370 deletions

View File

@ -6,34 +6,6 @@ use crate::{
use serde::{Deserialize, Serialize};
use vek::*;
pub const BLOCK_EFFICIENCY: f32 = 0.9;
/// Each section of this struct determines what damage is applied to a
/// particular target, using some identifier
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Damages {
/// Targets enemies, and all other creatures not in your group
pub enemy: Option<Damage>,
/// Targets people in the same group as you, and any pets you have
pub group: Option<Damage>,
}
impl Damages {
pub fn new(enemy: Option<Damage>, group: Option<Damage>) -> Self { Damages { enemy, group } }
pub fn get_damage(self, group_target: GroupTarget) -> Option<Damage> {
match group_target {
GroupTarget::InGroup => self.group,
GroupTarget::OutOfGroup => self.enemy,
}
}
pub fn contains_damage(self, source: DamageSource) -> bool {
self.enemy.map_or(false, |e| e.source == source)
|| self.group.map_or(false, |g| g.source == source)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum GroupTarget {
InGroup,
@ -58,12 +30,7 @@ pub struct Damage {
}
impl Damage {
pub fn modify_damage(
self,
block: bool,
loadout: Option<&Loadout>,
uid: Option<Uid>,
) -> HealthChange {
pub fn modify_damage(self, loadout: Option<&Loadout>, uid: Option<Uid>) -> HealthChange {
let mut damage = self.value;
match self.source {
DamageSource::Melee => {
@ -72,10 +39,6 @@ impl Damage {
if rand::random() {
critdamage = damage * 0.3;
}
// Block
if block {
damage *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction());
damage *= 1.0 - damage_reduction;
@ -95,10 +58,6 @@ impl Damage {
if rand::random() {
damage *= 1.2;
}
// Block
if block {
damage *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction());
damage *= 1.0 - damage_reduction;
@ -109,10 +68,6 @@ impl Damage {
}
},
DamageSource::Explosion => {
// Block
if block {
damage *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction());
damage *= 1.0 - damage_reduction;

View File

@ -1,4 +1,4 @@
use crate::{sync::Uid, Damages};
use crate::{sync::Uid, Damage, GroupTarget};
use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage};
use specs_idvs::IdvStorage;
@ -8,7 +8,7 @@ use std::time::Duration;
pub struct Properties {
pub angle: f32,
pub speed: f32,
pub damages: Damages,
pub damages: Vec<(Option<GroupTarget>, Damage)>,
pub lifesteal_eff: f32,
pub energy_regen: u32,
pub energy_cost: u32,

View File

@ -3,7 +3,7 @@ use crate::{
event::{LocalEvent, ServerEvent},
states::*,
sys::character_behavior::JoinData,
Damages, Knockback,
Damage, GroupTarget, Knockback,
};
use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage, VecStorage};
@ -155,9 +155,9 @@ impl Component for CharacterState {
type Storage = FlaggedStorage<Self, IdvStorage<Self>>;
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Attacking {
pub damages: Damages,
pub damages: Vec<(Option<GroupTarget>, Damage)>,
pub range: f32,
pub max_angle: f32,
pub applied: bool,

View File

@ -7,8 +7,9 @@ use crate::{
buff::{Buff, BuffCategory, BuffData, BuffKind, BuffSource},
projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile,
},
effect::Effect,
states::combo_melee,
Damage, DamageSource, Damages, Explosion, Knockback, RadiusEffect,
Damage, DamageSource, Explosion, GroupTarget, Knockback, RadiusEffect,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -302,13 +303,10 @@ impl Tool {
projectile: Projectile {
hit_solid: vec![projectile::Effect::Stick],
hit_entity: vec![
projectile::Effect::Damages(Damages::new(
Some(Damage {
source: DamageSource::Projectile,
value: 40.0 * self.base_power(),
}),
None,
)),
projectile::Effect::Damage(Some(GroupTarget::OutOfGroup), Damage {
source: DamageSource::Projectile,
value: 40.0 * self.base_power(),
}),
projectile::Effect::Knockback(Knockback::Away(10.0)),
projectile::Effect::RewardEnergy(50),
projectile::Effect::Vanish,
@ -360,13 +358,10 @@ impl Tool {
projectile: Projectile {
hit_solid: vec![projectile::Effect::Stick],
hit_entity: vec![
projectile::Effect::Damages(Damages::new(
Some(Damage {
source: DamageSource::Projectile,
value: 40.0 * self.base_power(),
}),
None,
)),
projectile::Effect::Damage(Some(GroupTarget::OutOfGroup), Damage {
source: DamageSource::Projectile,
value: 40.0 * self.base_power(),
}),
projectile::Effect::Knockback(Knockback::Away(10.0)),
projectile::Effect::Vanish,
projectile::Effect::Buff {
@ -425,16 +420,22 @@ impl Tool {
projectile: Projectile {
hit_solid: vec![
projectile::Effect::Explode(Explosion {
effects: vec![RadiusEffect::Damages(Damages::new(
Some(Damage {
source: DamageSource::Explosion,
value: 50.0 * self.base_power(),
}),
Some(Damage {
source: DamageSource::Healing,
value: 140.0 * self.base_power(),
}),
))],
effects: vec![
RadiusEffect::Entity(
Some(GroupTarget::OutOfGroup),
Effect::Damage(Damage {
source: DamageSource::Explosion,
value: 50.0 * self.base_power(),
}),
),
RadiusEffect::Entity(
Some(GroupTarget::InGroup),
Effect::Damage(Damage {
source: DamageSource::Healing,
value: 140.0 * self.base_power(),
}),
),
],
radius: 3.0 + 2.5 * self.base_power(),
energy_regen: 0,
}),
@ -442,16 +443,22 @@ impl Tool {
],
hit_entity: vec![
projectile::Effect::Explode(Explosion {
effects: vec![RadiusEffect::Damages(Damages::new(
Some(Damage {
source: DamageSource::Explosion,
value: 50.0 * self.base_power(),
}),
Some(Damage {
source: DamageSource::Healing,
value: 140.0 * self.base_power(),
}),
))],
effects: vec![
RadiusEffect::Entity(
Some(GroupTarget::OutOfGroup),
Effect::Damage(Damage {
source: DamageSource::Explosion,
value: 50.0 * self.base_power(),
}),
),
RadiusEffect::Entity(
Some(GroupTarget::InGroup),
Effect::Damage(Damage {
source: DamageSource::Healing,
value: 140.0 * self.base_power(),
}),
),
],
radius: 3.0 + 2.5 * self.base_power(),
energy_regen: 0,
}),
@ -478,13 +485,13 @@ impl Tool {
projectile: Projectile {
hit_solid: vec![
projectile::Effect::Explode(Explosion {
effects: vec![RadiusEffect::Damages(Damages::new(
Some(Damage {
effects: vec![RadiusEffect::Entity(
Some(GroupTarget::OutOfGroup),
Effect::Damage(Damage {
source: DamageSource::Explosion,
value: 100.0 * self.base_power(),
}),
None,
))],
)],
radius: 5.0,
energy_regen: 50,
}),
@ -492,13 +499,13 @@ impl Tool {
],
hit_entity: vec![
projectile::Effect::Explode(Explosion {
effects: vec![RadiusEffect::Damages(Damages::new(
Some(Damage {
effects: vec![RadiusEffect::Entity(
Some(GroupTarget::OutOfGroup),
Effect::Damage(Damage {
source: DamageSource::Explosion,
value: 100.0 * self.base_power(),
}),
None,
))],
)],
radius: 5.0,
energy_regen: 50,
}),

View File

@ -1,4 +1,4 @@
use crate::{comp::Buff, sync::Uid, Damages, Explosion, Knockback};
use crate::{comp::Buff, sync::Uid, Damage, Explosion, GroupTarget, Knockback};
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 {
Damages(Damages),
Damage(Option<GroupTarget>, Damage),
Knockback(Knockback),
RewardEnergy(u32),
Explode(Explosion),

View File

@ -1,4 +1,4 @@
use crate::{sync::Uid, Damages, Knockback};
use crate::{sync::Uid, Damage, GroupTarget, Knockback};
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 damages: Damages,
pub damages: Vec<(Option<GroupTarget>, Damage)>,
pub knockback: Knockback,
pub requires_ground: bool,
pub duration: Duration,

View File

@ -1,11 +1,12 @@
use crate::comp;
use crate::{combat, comp};
use serde::{Deserialize, Serialize};
/// An effect that may be applied to an entity
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Effect {
Health(comp::HealthChange),
Xp(i64),
Damage(combat::Damage),
}
impl Effect {
@ -13,6 +14,21 @@ impl Effect {
match self {
Effect::Health(c) => format!("{:+} health", c.amount),
Effect::Xp(n) => format!("{:+} exp", n),
Effect::Damage(d) => format!("{:+}", d.value),
}
}
pub fn modify_strength(&mut self, modifier: f32) {
match self {
Effect::Health(change) => {
change.amount = (change.amount as f32 * modifier) as i32;
},
Effect::Xp(amount) => {
*amount = (*amount as f32 * modifier) as i64;
},
Effect::Damage(damage) => {
damage.interpolate_damage(modifier, 0.0);
},
}
}
}

View File

@ -1,7 +1,4 @@
use crate::{
combat::{Damages, GroupTarget},
effect::Effect,
};
use crate::{combat::GroupTarget, effect::Effect};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -13,7 +10,6 @@ pub struct Explosion {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum RadiusEffect {
Damages(Damages),
TerrainDestruction(f32),
Entity(Option<GroupTarget>, Effect),
}

View File

@ -53,6 +53,6 @@ pub mod util;
pub mod vol;
pub mod volumes;
pub use combat::{Damage, DamageSource, Damages, GroupTarget, Knockback};
pub use combat::{Damage, DamageSource, GroupTarget, Knockback};
pub use explosion::{Explosion, RadiusEffect};
pub use loadout_builder::LoadoutBuilder;

View File

@ -6,7 +6,7 @@ use crate::{
states::utils::*,
sync::Uid,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, DamageSource, Damages,
Damage, DamageSource, GroupTarget,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -126,7 +126,10 @@ impl CharacterBehavior for Data {
let properties = beam::Properties {
angle: self.static_data.max_angle.to_radians(),
speed,
damages: Damages::new(Some(damage), Some(heal)),
damages: vec![
(Some(GroupTarget::OutOfGroup), damage),
(Some(GroupTarget::InGroup), heal),
],
lifesteal_eff: self.static_data.lifesteal_eff,
energy_regen,
energy_cost,

View File

@ -2,7 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, EnergyChange, EnergySource, StateUpdate},
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, DamageSource, Damages, Knockback,
Damage, DamageSource, GroupTarget, Knockback,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -76,13 +76,10 @@ impl CharacterBehavior for Data {
// Hit attempt
data.updater.insert(data.entity, Attacking {
damages: Damages::new(
Some(Damage {
source: DamageSource::Melee,
value: self.static_data.base_damage as f32,
}),
None,
),
damages: vec![(Some(GroupTarget::OutOfGroup), Damage {
source: DamageSource::Melee,
value: self.static_data.base_damage as f32,
})],
range: self.static_data.range,
max_angle: 180_f32.to_radians(),
applied: false,

View File

@ -2,7 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, EnergyChange, EnergySource, StateUpdate},
states::utils::{StageSection, *},
sys::character_behavior::*,
Damage, DamageSource, Damages, Knockback,
Damage, DamageSource, GroupTarget, Knockback,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -125,7 +125,7 @@ impl CharacterBehavior for Data {
// Hit attempt
data.updater.insert(data.entity, Attacking {
damages: Damages::new(Some(damage), None),
damages: vec![(Some(GroupTarget::OutOfGroup), damage)],
range: self.static_data.range,
max_angle: self.static_data.max_angle.to_radians(),
applied: false,

View File

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

View File

@ -2,7 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, EnergyChange, EnergySource, StateUpdate},
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, DamageSource, Damages, Knockback,
Damage, DamageSource, GroupTarget, Knockback,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -131,13 +131,10 @@ impl CharacterBehavior for Data {
* self.static_data.stage_data[stage_index].damage_increase,
);
data.updater.insert(data.entity, Attacking {
damages: Damages::new(
Some(Damage {
source: DamageSource::Melee,
value: damage as f32,
}),
None,
),
damages: vec![(Some(GroupTarget::OutOfGroup), Damage {
source: DamageSource::Melee,
value: damage as f32,
})],
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,7 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, EnergyChange, EnergySource, StateUpdate},
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, DamageSource, Damages, Knockback,
Damage, DamageSource, GroupTarget, Knockback,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -139,7 +139,7 @@ impl CharacterBehavior for Data {
* charge_frac
+ self.static_data.base_knockback;
data.updater.insert(data.entity, Attacking {
damages: Damages::new(Some(damage), None),
damages: vec![(Some(GroupTarget::OutOfGroup), damage)],
range: self.static_data.range,
max_angle: self.static_data.angle.to_radians(),
applied: false,

View File

@ -2,7 +2,7 @@ use crate::{
comp::{Attacking, CharacterState, StateUpdate},
states::utils::{StageSection, *},
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, DamageSource, Damages, Knockback,
Damage, DamageSource, GroupTarget, Knockback,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -133,13 +133,10 @@ impl CharacterBehavior for Data {
if !self.exhausted {
// Hit attempt, when animation plays
data.updater.insert(data.entity, Attacking {
damages: Damages::new(
Some(Damage {
source: DamageSource::Melee,
value: self.static_data.base_damage as f32,
}),
None,
),
damages: vec![(Some(GroupTarget::OutOfGroup), Damage {
source: DamageSource::Melee,
value: self.static_data.base_damage as f32,
})],
range: self.static_data.range,
max_angle: self.static_data.max_angle.to_radians(),
applied: false,

View File

@ -3,7 +3,7 @@ use crate::{
event::ServerEvent,
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
Damage, DamageSource, Damages, Knockback,
Damage, DamageSource, GroupTarget, Knockback,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -70,13 +70,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,
damages: Damages::new(
Some(Damage {
source: DamageSource::Shockwave,
value: self.static_data.damage as f32,
}),
None,
),
damages: vec![(Some(GroupTarget::OutOfGroup), Damage {
source: DamageSource::Shockwave,
value: self.static_data.damage as f32,
})],
knockback: self.static_data.knockback,
requires_ground: self.static_data.requires_ground,
owner: Some(*data.uid),

View File

@ -5,7 +5,7 @@ use crate::{
character_behavior::{CharacterBehavior, JoinData},
phys::GRAVITY,
},
Damage, DamageSource, Damages, Knockback,
Damage, DamageSource, GroupTarget, Knockback,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -108,13 +108,10 @@ impl CharacterBehavior for Data {
});
// Hit attempt
data.updater.insert(data.entity, Attacking {
damages: Damages::new(
Some(Damage {
source: DamageSource::Melee,
value: self.static_data.base_damage as f32,
}),
None,
),
damages: vec![(Some(GroupTarget::OutOfGroup), Damage {
source: DamageSource::Melee,
value: self.static_data.base_damage as f32,
})],
range: self.static_data.range,
max_angle: 180_f32.to_radians(),
applied: false,

View File

@ -1,19 +1,17 @@
use crate::{
comp::{
group, Beam, BeamSegment, Body, CharacterState, Energy, EnergyChange, EnergySource, Health,
HealthChange, HealthSource, Last, Loadout, Ori, Pos, Scale,
group, Beam, BeamSegment, Body, Energy, EnergyChange, EnergySource, Health, HealthChange,
HealthSource, Last, Loadout, Ori, Pos, Scale,
},
event::{EventBus, ServerEvent},
state::{DeltaTime, Time},
sync::{Uid, UidAllocator},
DamageSource, GroupTarget,
GroupTarget,
};
use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage};
use std::time::Duration;
use vek::*;
pub const BLOCK_ANGLE: f32 = 180.0;
/// This system is responsible for handling beams that heal or do damage
pub struct Sys;
impl<'a> System<'a> for Sys {
@ -33,7 +31,6 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Health>,
ReadStorage<'a, Loadout>,
ReadStorage<'a, group::Group>,
ReadStorage<'a, CharacterState>,
ReadStorage<'a, Energy>,
WriteStorage<'a, BeamSegment>,
WriteStorage<'a, Beam>,
@ -56,7 +53,6 @@ impl<'a> System<'a> for Sys {
healths,
loadouts,
groups,
character_states,
energies,
mut beam_segments,
mut beams,
@ -116,25 +112,13 @@ impl<'a> System<'a> for Sys {
};
// Go through all other effectable entities
for (
b,
uid_b,
pos_b,
last_pos_b_maybe,
ori_b,
scale_b_maybe,
character_b,
health_b,
body_b,
) in (
for (b, uid_b, pos_b, last_pos_b_maybe, scale_b_maybe, health_b, body_b) in (
&entities,
&uids,
&positions,
// TODO: make sure that these are maintained on the client and remove `.maybe()`
last_positions.maybe(),
&orientations,
scales.maybe(),
character_states.maybe(),
&healths,
&bodies,
)
@ -174,58 +158,63 @@ impl<'a> System<'a> for Sys {
continue;
}
let damage = if let Some(damage) = beam_segment.damages.get_damage(target_group)
{
damage
} else {
continue;
};
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;
let change = damage.modify_damage(block, loadouts.get(b), beam_segment.owner);
if !matches!(damage.source, DamageSource::Healing) {
server_emitter.emit(ServerEvent::Damage { entity: b, change });
if let Some(entity) = beam_owner {
if beam_segment.lifesteal_eff > 0.0 {
server_emitter.emit(ServerEvent::Damage {
entity,
change: HealthChange {
amount: (-change.amount as f32 * beam_segment.lifesteal_eff)
as i32,
cause: HealthSource::Healing {
by: beam_segment.owner,
},
},
});
for (target, damage) in beam_segment.damages.iter() {
if let Some(target) = target {
if *target != target_group {
continue;
}
server_emitter.emit(ServerEvent::EnergyChange {
entity,
change: EnergyChange {
amount: beam_segment.energy_regen as i32,
source: EnergySource::HitEnemy,
},
});
}
} else if let Some(energy) = beam_owner.and_then(|o| energies.get(o)) {
if energy.current() > beam_segment.energy_cost {
server_emitter.emit(ServerEvent::EnergyChange {
entity: beam_owner.unwrap(), /* If it's able to get an energy
* component, the entity exists */
change: EnergyChange {
amount: -(beam_segment.energy_cost as i32), // Stamina use
source: EnergySource::Ability,
},
});
server_emitter.emit(ServerEvent::Damage { entity: b, change });
// Modify damage
let change = damage.modify_damage(loadouts.get(b), beam_segment.owner);
match target {
Some(GroupTarget::OutOfGroup) => {
server_emitter.emit(ServerEvent::Damage { entity: b, change });
if let Some(entity) = beam_owner {
server_emitter.emit(ServerEvent::Damage {
entity,
change: HealthChange {
amount: (-change.amount as f32
* beam_segment.lifesteal_eff)
as i32,
cause: HealthSource::Healing {
by: beam_segment.owner,
},
},
});
server_emitter.emit(ServerEvent::EnergyChange {
entity,
change: EnergyChange {
amount: beam_segment.energy_regen as i32,
source: EnergySource::HitEnemy,
},
});
}
},
Some(GroupTarget::InGroup) => {
if let Some(energy) = beam_owner.and_then(|o| energies.get(o)) {
if energy.current() > beam_segment.energy_cost {
server_emitter.emit(ServerEvent::EnergyChange {
entity: beam_owner.unwrap(), /* If it's able to get an energy
* component, the entity exists */
change: EnergyChange {
amount: -(beam_segment.energy_cost as i32), // Stamina use
source: EnergySource::Ability,
},
});
server_emitter
.emit(ServerEvent::Damage { entity: b, change });
}
}
},
None => {},
}
// Adds entities that were hit to the hit_entities list on the beam, sees if
// it needs to purge the hit_entities list
hit_entities.push(*uid_b);
}
// Adds entities that were hit to the hit_entities list on the beam, sees if it
// needs to purge the hit_entities list
hit_entities.push(*uid_b);
}
}
}

View File

@ -1,5 +1,5 @@
use crate::{
comp::{buff, group, Attacking, Body, CharacterState, Health, Loadout, Ori, Pos, Scale},
comp::{buff, group, Attacking, Body, Health, Loadout, Ori, Pos, Scale},
event::{EventBus, LocalEvent, ServerEvent},
metrics::SysMetrics,
span,
@ -12,8 +12,6 @@ use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}
use std::time::Duration;
use vek::*;
pub const BLOCK_ANGLE: f32 = 180.0;
/// This system is responsible for handling accepted inputs like moving or
/// attacking
pub struct Sys;
@ -32,7 +30,6 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Health>,
ReadStorage<'a, Loadout>,
ReadStorage<'a, group::Group>,
ReadStorage<'a, CharacterState>,
WriteStorage<'a, Attacking>,
);
@ -51,7 +48,6 @@ impl<'a> System<'a> for Sys {
healths,
loadouts,
groups,
character_states,
mut attacking_storage,
): Self::SystemData,
) {
@ -76,16 +72,8 @@ impl<'a> System<'a> for Sys {
attack.applied = true;
// Go through all other entities
for (b, pos_b, ori_b, scale_b_maybe, character_b, health_b, body_b) in (
&entities,
&positions,
&orientations,
scales.maybe(),
character_states.maybe(),
&healths,
&bodies,
)
.join()
for (b, pos_b, scale_b_maybe, health_b, body_b) in
(&entities, &positions, scales.maybe(), &healths, &bodies).join()
{
// 2D versions
let pos2 = Vec2::from(pos.0);
@ -116,20 +104,16 @@ impl<'a> System<'a> for Sys {
GroupTarget::OutOfGroup
};
let damage = if let Some(damage) = attack.damages.get_damage(target_group) {
damage
} else {
continue;
};
for (target, damage) in attack.damages.iter() {
if let Some(target) = target {
if *target != target_group {
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;
let change = damage.modify_damage(loadouts.get(b), Some(*uid));
let change = damage.modify_damage(block, loadouts.get(b), Some(*uid));
if change.amount != 0 {
server_emitter.emit(ServerEvent::Damage { entity: b, change });
// Apply bleeding buff on melee hits with 10% chance
// TODO: Don't have buff uniformly applied on all melee attacks
if change.amount < 0 && thread_rng().gen::<f32>() < 0.1 {
@ -147,15 +131,13 @@ impl<'a> System<'a> for Sys {
)),
});
}
attack.hit_count += 1;
}
if change.amount != 0 {
let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0));
let impulse = attack.knockback.calculate_impulse(kb_dir);
if !impulse.is_approx_zero() {
server_emitter.emit(ServerEvent::Knockback { entity: b, impulse });
}
attack.hit_count += 1;
}
}
}

View File

@ -104,31 +104,27 @@ impl<'a> System<'a> for Sys {
for effect in projectile.hit_entity.drain(..) {
match effect {
projectile::Effect::Damages(damages) => {
projectile::Effect::Damage(target, damage) => {
if Some(other) == projectile.owner {
continue;
}
let damage = if let Some(damage) = damages.get_damage(target_group) {
damage
} else {
continue;
};
if let Some(target) = target {
if target != target_group {
continue;
}
}
if let Some(other_entity) =
uid_allocator.retrieve_entity_internal(other.into())
{
let other_entity_loadout = loadouts.get(other_entity);
let change = damage.modify_damage(
false,
other_entity_loadout,
projectile.owner,
);
if change.amount != 0 {
server_emitter.emit(ServerEvent::Damage {
entity: other_entity,
change,
});
}
let change =
damage.modify_damage(other_entity_loadout, projectile.owner);
server_emitter.emit(ServerEvent::Damage {
entity: other_entity,
change,
});
}
},
projectile::Effect::Knockback(knockback) => {
@ -177,6 +173,7 @@ impl<'a> System<'a> for Sys {
}
}
},
// TODO: Change to effect after !1472 merges
projectile::Effect::Buff { buff, chance } => {
if let Some(entity) =
uid_allocator.retrieve_entity_internal(other.into())

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
group, Body, CharacterState, Health, HealthSource, Last, Loadout, Ori, PhysicsState, Pos,
Scale, Shockwave, ShockwaveHitEntities,
group, Body, Health, HealthSource, Last, Loadout, Ori, PhysicsState, Pos, Scale, Shockwave,
ShockwaveHitEntities,
},
event::{EventBus, LocalEvent, ServerEvent},
state::{DeltaTime, Time},
@ -12,8 +12,6 @@ use crate::{
use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage};
use vek::*;
pub const BLOCK_ANGLE: f32 = 180.0;
/// This system is responsible for handling accepted inputs like moving or
/// attacking
pub struct Sys;
@ -35,7 +33,6 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Health>,
ReadStorage<'a, Loadout>,
ReadStorage<'a, group::Group>,
ReadStorage<'a, CharacterState>,
ReadStorage<'a, PhysicsState>,
WriteStorage<'a, Shockwave>,
WriteStorage<'a, ShockwaveHitEntities>,
@ -59,7 +56,6 @@ impl<'a> System<'a> for Sys {
healths,
loadouts,
groups,
character_states,
physics_states,
mut shockwaves,
mut shockwave_hit_lists,
@ -131,9 +127,7 @@ impl<'a> System<'a> for Sys {
uid_b,
pos_b,
last_pos_b_maybe,
ori_b,
scale_b_maybe,
character_b,
health_b,
body_b,
physics_state_b,
@ -143,9 +137,7 @@ impl<'a> System<'a> for Sys {
&positions,
// TODO: make sure that these are maintained on the client and remove `.maybe()`
last_positions.maybe(),
&orientations,
scales.maybe(),
character_states.maybe(),
&healths,
&bodies,
&physics_states,
@ -199,21 +191,19 @@ impl<'a> System<'a> for Sys {
&& (!shockwave.requires_ground || physics_state_b.on_ground);
if hit {
let damage = if let Some(damage) = shockwave.damages.get_damage(target_group) {
damage
} else {
continue;
};
for (target, damage) in shockwave.damages.iter() {
if let Some(target) = target {
if *target != target_group {
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;
let owner_uid = shockwave.owner.unwrap_or(*uid);
let change = damage.modify_damage(loadouts.get(b), Some(owner_uid));
let owner_uid = shockwave.owner.unwrap_or(*uid);
let change = damage.modify_damage(block, loadouts.get(b), Some(owner_uid));
if change.amount != 0 {
server_emitter.emit(ServerEvent::Damage { entity: b, change });
shockwave_hit_list.hit_entities.push(*uid_b);
let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0));
let impulse = shockwave.knockback.calculate_impulse(kb_dir);
if !impulse.is_approx_zero() {

View File

@ -11,12 +11,12 @@ use common::{
object, Alignment, Body, Energy, EnergyChange, Group, Health, HealthChange, HealthSource,
Item, Player, Pos, Stats,
},
effect::Effect,
lottery::Lottery,
msg::{PlayerListUpdate, ServerGeneral},
outcome::Outcome,
state::BlockChange,
sync::{Uid, UidAllocator, WorldSyncExt},
sys::melee::BLOCK_ANGLE,
terrain::{Block, TerrainGrid},
vol::ReadVol,
Damage, DamageSource, Explosion, GroupTarget, RadiusEffect,
@ -457,7 +457,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
value: falldmg,
};
let loadouts = state.ecs().read_storage::<comp::Loadout>();
let change = damage.modify_damage(false, loadouts.get(entity), None);
let change = damage.modify_damage(loadouts.get(entity), None);
health.change_by(change);
}
}
@ -516,7 +516,7 @@ pub fn handle_explosion(
// Uses radius as outcome power, makes negative if explosion has healing effect
let outcome_power = explosion.radius
* if explosion.effects.iter().any(
|e| matches!(e, RadiusEffect::Damages(d) if d.contains_damage(DamageSource::Healing)),
|e| matches!(e, RadiusEffect::Entity(_, e) if matches!(e, Effect::Damage(d) if matches!(d.source, DamageSource::Healing))),
) {
-1.0
} else {
@ -530,7 +530,7 @@ pub fn handle_explosion(
is_attack: explosion
.effects
.iter()
.any(|e| matches!(e, RadiusEffect::Damages(_))),
.any(|e| matches!(e, RadiusEffect::Entity(_, e) if matches!(e, Effect::Damage(_)))),
reagent,
});
let owner_entity = owner.and_then(|uid| {
@ -541,70 +541,6 @@ pub fn handle_explosion(
for effect in explosion.effects {
match effect {
RadiusEffect::Damages(damages) => {
for (entity_b, pos_b, ori_b, character_b, health_b, loadout_b) in (
&ecs.entities(),
&ecs.read_storage::<comp::Pos>(),
&ecs.read_storage::<comp::Ori>(),
ecs.read_storage::<comp::CharacterState>().maybe(),
&mut ecs.write_storage::<comp::Health>(),
ecs.read_storage::<comp::Loadout>().maybe(),
)
.join()
{
let distance_squared = pos.distance_squared(pos_b.0);
// Check if it is a hit
if !health_b.is_dead
// RADIUS
&& distance_squared < explosion.radius.powi(2)
{
// See if entities are in the same group
let mut same_group = owner_entity
.and_then(|e| groups.get(e))
.map_or(false, |group_a| Some(group_a) == groups.get(entity_b));
if let Some(entity) = owner_entity {
if entity == entity_b {
same_group = true;
}
}
let target_group = if same_group {
GroupTarget::InGroup
} else {
GroupTarget::OutOfGroup
};
let mut damage = if let Some(damage) = damages.get_damage(target_group) {
damage
} else {
continue;
};
let strength = 1.0 - distance_squared / explosion.radius.powi(2);
damage.interpolate_damage(strength, 0.0);
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;
let change = damage.modify_damage(block, loadout_b, owner);
if change.amount != 0 {
health_b.change_by(change);
if let Some(owner) = owner_entity {
if let Some(energy) =
ecs.write_storage::<comp::Energy>().get_mut(owner)
{
energy.change_by(EnergyChange {
amount: explosion.energy_regen as i32,
source: comp::EnergySource::HitEnemy,
});
}
}
}
}
}
},
RadiusEffect::TerrainDestruction(power) => {
const RAYS: usize = 500;
@ -671,10 +607,14 @@ pub fn handle_explosion(
.cast();
}
},
RadiusEffect::Entity(target_group, effect) => {
for (entity_b, pos_b) in (&ecs.entities(), &ecs.read_storage::<comp::Pos>()).join()
RadiusEffect::Entity(target, mut effect) => {
for (entity_b, pos_b, health_b) in (
&ecs.entities(),
&ecs.read_storage::<comp::Pos>(),
&ecs.read_storage::<comp::Health>(),
)
.join()
{
let distance_squared = pos.distance_squared(pos_b.0);
// See if entities are in the same group
let mut same_group = owner_entity
.and_then(|e| groups.get(e))
@ -684,16 +624,34 @@ pub fn handle_explosion(
same_group = true;
}
}
let hit_group = if same_group {
let target_group = if same_group {
GroupTarget::InGroup
} else {
GroupTarget::OutOfGroup
};
if distance_squared < explosion.radius.powi(2)
&& target_group.map_or(true, |g| g == hit_group)
{
server.state().apply_effect(entity_b, effect);
if let Some(target) = target {
if target != target_group {
continue;
}
}
let distance_squared = pos.distance_squared(pos_b.0);
let strength = 1.0 - distance_squared / explosion.radius.powi(2);
if strength > 0.0 && !health_b.is_dead {
effect.modify_strength(strength);
server.state().apply_effect(entity_b, effect, owner);
// Apply energy change
if let Some(owner) = owner_entity {
if let Some(energy) = ecs.write_storage::<comp::Energy>().get_mut(owner)
{
energy.change_by(EnergyChange {
amount: explosion.energy_regen as i32,
source: comp::EnergySource::HitEnemy,
});
}
}
}
}
},

View File

@ -349,7 +349,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
drop(inventories);
if let Some(effect) = maybe_effect {
state.apply_effect(entity, effect);
state.apply_effect(entity, effect, None);
}
if let Some(event) = event {
state.write_component(entity, comp::InventoryUpdate::new(event));

View File

@ -20,7 +20,7 @@ use vek::*;
pub trait StateExt {
/// Updates a component associated with the entity based on the `Effect`
fn apply_effect(&self, entity: EcsEntity, effect: Effect);
fn apply_effect(&self, entity: EcsEntity, effect: Effect, source: Option<Uid>);
/// Build a non-player character
fn create_npc(
&mut self,
@ -71,7 +71,7 @@ pub trait StateExt {
}
impl StateExt for State {
fn apply_effect(&self, entity: EcsEntity, effect: Effect) {
fn apply_effect(&self, entity: EcsEntity, effect: Effect, source: Option<Uid>) {
match effect {
Effect::Health(change) => {
self.ecs()
@ -85,6 +85,16 @@ impl StateExt for State {
.get_mut(entity)
.map(|stats| stats.exp.change_by(xp));
},
Effect::Damage(damage) => {
#[allow(irrefutable_let_patterns)]
if let loadout = self.ecs().read_storage::<comp::Loadout>().get(entity) {
let change = damage.modify_damage(loadout, source);
self.ecs()
.write_storage::<comp::Health>()
.get_mut(entity)
.map(|health| health.change_by(change));
}
},
}
}