Energy and health change server events now reference EcsEntity instead of Uid. Added TargetGroup to use to determine targets of effects/damage. Added Entity(TargetGroup, Effect) to RadiusEffect enum.

This commit is contained in:
Sam 2020-11-01 18:26:01 -06:00
parent 87bff41a66
commit d38f1d319c
14 changed files with 172 additions and 104 deletions

View File

@ -21,8 +21,11 @@ pub struct Damages {
impl Damages {
pub fn new(enemy: Option<Damage>, group: Option<Damage>) -> Self { Damages { enemy, group } }
pub fn get_damage(self, same_group: bool) -> Option<Damage> {
if same_group { self.group } else { self.enemy }
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 {
@ -31,6 +34,12 @@ impl Damages {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum GroupTarget {
InGroup,
OutOfGroup,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum DamageSource {
Melee,

View File

@ -34,7 +34,7 @@ pub enum ServerEvent {
reagent: Option<Reagent>,
},
Damage {
uid: Uid,
entity: EcsEntity,
change: comp::HealthChange,
},
Destroy {
@ -111,7 +111,7 @@ pub enum ServerEvent {
buff_change: comp::BuffChange,
},
EnergyChange {
uid: Uid,
entity: EcsEntity,
change: comp::EnergyChange,
},
}

View File

@ -1,4 +1,7 @@
use crate::{combat::Damages, effect::Effect};
use crate::{
combat::{Damages, GroupTarget},
effect::Effect,
};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -12,5 +15,5 @@ pub struct Explosion {
pub enum RadiusEffect {
Damages(Damages),
TerrainDestruction(f32),
EntityEffect(Effect),
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, Knockback};
pub use combat::{Damage, DamageSource, Damages, GroupTarget, Knockback};
pub use explosion::{Explosion, RadiusEffect};
pub use loadout_builder::LoadoutBuilder;

View File

@ -6,7 +6,7 @@ use crate::{
event::{EventBus, ServerEvent},
state::{DeltaTime, Time},
sync::{Uid, UidAllocator},
DamageSource,
DamageSource, GroupTarget,
};
use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage};
use std::time::Duration;
@ -68,8 +68,8 @@ impl<'a> System<'a> for Sys {
let dt = dt.0;
// Beams
for (entity, uid, pos, ori, beam_segment) in
(&entities, &uids, &positions, &orientations, &beam_segments).join()
for (entity, pos, ori, beam_segment) in
(&entities, &positions, &orientations, &beam_segments).join()
{
let creation_time = match beam_segment.creation {
Some(time) => time,
@ -163,12 +163,19 @@ impl<'a> System<'a> for Sys {
.map(|group_a| Some(group_a) == groups.get(b))
.unwrap_or(Some(*uid_b) == beam_segment.owner);
let target_group = if same_group {
GroupTarget::InGroup
} else {
GroupTarget::OutOfGroup
};
// If owner, shouldn't heal or damage
if Some(*uid_b) == beam_segment.owner {
continue;
}
let damage = if let Some(damage) = beam_segment.damages.get_damage(same_group) {
let damage = if let Some(damage) = beam_segment.damages.get_damage(target_group)
{
damage
} else {
continue;
@ -181,25 +188,22 @@ impl<'a> System<'a> for Sys {
let change = damage.modify_damage(block, loadouts.get(b), beam_segment.owner);
if !matches!(damage.source, DamageSource::Healing) {
server_emitter.emit(ServerEvent::Damage {
uid: *uid_b,
change,
});
if beam_segment.lifesteal_eff > 0.0 {
server_emitter.emit(ServerEvent::Damage {
uid: beam_segment.owner.unwrap_or(*uid),
change: HealthChange {
amount: (-change.amount as f32 * beam_segment.lifesteal_eff)
as i32,
cause: HealthSource::Healing {
by: beam_segment.owner,
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,
},
},
},
});
}
if let Some(uid) = beam_segment.owner {
});
}
server_emitter.emit(ServerEvent::EnergyChange {
uid,
entity,
change: EnergyChange {
amount: beam_segment.energy_regen as i32,
source: EnergySource::HitEnemy,
@ -208,19 +212,15 @@ impl<'a> System<'a> for Sys {
}
} else if let Some(energy) = beam_owner.and_then(|o| energies.get(o)) {
if energy.current() > beam_segment.energy_cost {
if let Some(uid) = beam_segment.owner {
server_emitter.emit(ServerEvent::EnergyChange {
uid,
change: EnergyChange {
amount: -(beam_segment.energy_cost as i32), // Stamina use
source: EnergySource::Ability,
},
})
}
server_emitter.emit(ServerEvent::Damage {
uid: *uid_b,
change,
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 });
}
}
// Adds entities that were hit to the hit_entities list on the beam, sees if it

View File

@ -5,7 +5,6 @@ use crate::{
},
event::{EventBus, ServerEvent},
state::DeltaTime,
sync::Uid,
};
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
use std::time::Duration;
@ -17,7 +16,6 @@ impl<'a> System<'a> for Sys {
Entities<'a>,
Read<'a, DeltaTime>,
Read<'a, EventBus<ServerEvent>>,
ReadStorage<'a, Uid>,
ReadStorage<'a, Loadout>,
WriteStorage<'a, Health>,
WriteStorage<'a, Buffs>,
@ -25,14 +23,13 @@ impl<'a> System<'a> for Sys {
fn run(
&mut self,
(entities, dt, server_bus, uids, loadouts, mut healths, mut buffs): Self::SystemData,
(entities, dt, server_bus, loadouts, mut healths, mut buffs): Self::SystemData,
) {
let mut server_emitter = server_bus.emitter();
// Set to false to avoid spamming server
buffs.set_event_emission(false);
healths.set_event_emission(false);
for (entity, buff_comp, uid, health) in (&entities, &mut buffs, &uids, &mut healths).join()
{
for (entity, buff_comp, health) in (&entities, &mut buffs, &mut healths).join() {
let mut expired_buffs = Vec::<BuffId>::new();
for (id, buff) in buff_comp.buffs.iter_mut() {
// Tick the buff and subtract delta from it
@ -94,7 +91,7 @@ impl<'a> System<'a> for Sys {
HealthSource::Buff { owner: buff_owner }
};
server_emitter.emit(ServerEvent::Damage {
uid: *uid,
entity,
change: HealthChange {
amount: *accumulated as i32,
cause,

View File

@ -5,6 +5,7 @@ use crate::{
span,
sync::Uid,
util::Dir,
GroupTarget,
};
use rand::{thread_rng, Rng};
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
@ -75,9 +76,8 @@ impl<'a> System<'a> for Sys {
attack.applied = true;
// Go through all other entities
for (b, uid_b, pos_b, ori_b, scale_b_maybe, character_b, health_b, body_b) in (
for (b, pos_b, ori_b, scale_b_maybe, character_b, health_b, body_b) in (
&entities,
&uids,
&positions,
&orientations,
scales.maybe(),
@ -110,7 +110,13 @@ impl<'a> System<'a> for Sys {
.map(|group_a| Some(group_a) == groups.get(b))
.unwrap_or(false);
let damage = if let Some(damage) = attack.damages.get_damage(same_group) {
let target_group = if same_group {
GroupTarget::InGroup
} else {
GroupTarget::OutOfGroup
};
let damage = if let Some(damage) = attack.damages.get_damage(target_group) {
damage
} else {
continue;
@ -122,10 +128,7 @@ impl<'a> System<'a> for Sys {
let change = damage.modify_damage(block, loadouts.get(b), Some(*uid));
if change.amount != 0 {
server_emitter.emit(ServerEvent::Damage {
uid: *uid_b,
change,
});
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

View File

@ -9,6 +9,7 @@ use crate::{
span,
state::DeltaTime,
sync::UidAllocator,
GroupTarget,
};
use rand::{thread_rng, Rng};
use specs::{
@ -83,6 +84,13 @@ impl<'a> System<'a> for Sys {
.retrieve_entity_internal(other.into())
.and_then(|e| groups.get(e))
);
let target_group = if same_group {
GroupTarget::InGroup
} else {
GroupTarget::OutOfGroup
};
if projectile.ignore_group
// Skip if in the same group
&& same_group
@ -100,36 +108,49 @@ impl<'a> System<'a> for Sys {
if Some(other) == projectile.owner {
continue;
}
let damage = if let Some(damage) = damages.get_damage(same_group) {
let damage = if let Some(damage) = damages.get_damage(target_group) {
damage
} 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 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 { uid: other, change });
if change.amount != 0 {
server_emitter.emit(ServerEvent::Damage {
entity: other_entity,
change,
});
}
}
},
projectile::Effect::Knockback(knockback) => {
if let Some(entity) =
if let Some(other_entity) =
uid_allocator.retrieve_entity_internal(other.into())
{
let impulse = knockback.calculate_impulse(ori.0);
if !impulse.is_approx_zero() {
local_emitter
.emit(LocalEvent::ApplyImpulse { entity, impulse });
local_emitter.emit(LocalEvent::ApplyImpulse {
entity: other_entity,
impulse,
});
}
}
},
projectile::Effect::RewardEnergy(energy) => {
if let Some(uid) = projectile.owner {
if let Some(entity_owner) = projectile
.owner
.and_then(|u| uid_allocator.retrieve_entity_internal(u.into()))
{
server_emitter.emit(ServerEvent::EnergyChange {
uid,
entity: entity_owner,
change: EnergyChange {
amount: energy as i32,
source: EnergySource::HitEnemy,

View File

@ -7,6 +7,7 @@ use crate::{
state::{DeltaTime, Time},
sync::{Uid, UidAllocator},
util::Dir,
GroupTarget,
};
use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage};
use vek::*;
@ -177,6 +178,12 @@ impl<'a> System<'a> for Sys {
.map(|group_a| Some(group_a) == groups.get(b))
.unwrap_or(Some(*uid_b) == shockwave.owner);
let target_group = if same_group {
GroupTarget::InGroup
} else {
GroupTarget::OutOfGroup
};
// Check if it is a hit
let hit = entity != b
&& !health_b.is_dead
@ -192,7 +199,7 @@ 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(same_group) {
let damage = if let Some(damage) = shockwave.damages.get_damage(target_group) {
damage
} else {
continue;
@ -205,10 +212,7 @@ impl<'a> System<'a> for Sys {
let change = damage.modify_damage(block, loadouts.get(b), Some(owner_uid));
if change.amount != 0 {
server_emitter.emit(ServerEvent::Damage {
uid: *uid_b,
change,
});
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);

View File

@ -1158,10 +1158,13 @@ fn handle_explosion(
pos: pos.0,
explosion: Explosion {
effects: vec![
RadiusEffect::EntityEffect(Effect::Health(comp::HealthChange {
amount: (-100.0 * power) as i32,
cause: comp::HealthSource::Explosion { owner: None },
})),
RadiusEffect::Entity(
None,
Effect::Health(comp::HealthChange {
amount: (-100.0 * power) as i32,
cause: comp::HealthSource::Explosion { owner: None },
}),
),
RadiusEffect::TerrainDestruction(power),
],
radius: 3.0 * power,

View File

@ -19,7 +19,7 @@ use common::{
sys::melee::BLOCK_ANGLE,
terrain::{Block, TerrainGrid},
vol::ReadVol,
Damage, DamageSource, Explosion, RadiusEffect,
Damage, DamageSource, Explosion, GroupTarget, RadiusEffect,
};
use comp::item::Reagent;
use rand::prelude::*;
@ -27,12 +27,10 @@ use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt
use tracing::error;
use vek::Vec3;
pub fn handle_damage(server: &Server, uid: Uid, change: HealthChange) {
pub fn handle_damage(server: &Server, entity: EcsEntity, change: HealthChange) {
let ecs = &server.state.ecs();
if let Some(entity) = ecs.entity_from_uid(uid.into()) {
if let Some(health) = ecs.write_storage::<Health>().get_mut(entity) {
health.change_by(change);
}
if let Some(health) = ecs.write_storage::<Health>().get_mut(entity) {
health.change_by(change);
}
}
@ -570,7 +568,13 @@ pub fn handle_explosion(
}
}
let mut damage = if let Some(damage) = damages.get_damage(same_group) {
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;
@ -667,13 +671,29 @@ pub fn handle_explosion(
.cast();
}
},
RadiusEffect::EntityEffect(effect) => {
for (entity, pos_entity) in
(&ecs.entities(), &ecs.read_storage::<comp::Pos>()).join()
RadiusEffect::Entity(target_group, effect) => {
for (entity_b, pos_b) in (&ecs.entities(), &ecs.read_storage::<comp::Pos>()).join()
{
let distance_squared = pos.distance_squared(pos_entity.0);
if distance_squared < explosion.radius.powi(2) {
server.state().apply_effect(entity, effect);
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))
.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 hit_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);
}
}
},
@ -754,11 +774,9 @@ pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::Bu
}
}
pub fn handle_energy_change(server: &Server, uid: Uid, change: EnergyChange) {
pub fn handle_energy_change(server: &Server, entity: EcsEntity, change: EnergyChange) {
let ecs = &server.state.ecs();
if let Some(entity) = ecs.entity_from_uid(uid.into()) {
if let Some(energy) = ecs.write_storage::<Energy>().get_mut(entity) {
energy.change_by(change);
}
if let Some(energy) = ecs.write_storage::<Energy>().get_mut(entity) {
energy.change_by(change);
}
}

View File

@ -82,7 +82,7 @@ impl Server {
ServerEvent::Knockback { entity, impulse } => {
handle_knockback(&self, entity, impulse)
},
ServerEvent::Damage { uid, change } => handle_damage(&self, uid, change),
ServerEvent::Damage { entity, change } => handle_damage(&self, entity, change),
ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause),
ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip),
ServerEvent::GroupManip(entity, manip) => handle_group(self, entity, manip),
@ -137,8 +137,8 @@ impl Server {
entity,
buff_change,
} => handle_buff(self, entity, buff_change),
ServerEvent::EnergyChange { uid, change } => {
handle_energy_change(&self, uid, change)
ServerEvent::EnergyChange { entity, change } => {
handle_energy_change(&self, entity, change)
},
}
}

View File

@ -242,6 +242,10 @@ impl StateExt for State {
z_max: body.height(),
});
self.write_component(entity, body);
self.write_component(
entity,
comp::Health::new(stats.body_type, stats.level.level()),
);
self.write_component(entity, stats);
self.write_component(entity, inventory);
self.write_component(entity, loadout);

View File

@ -50,10 +50,13 @@ impl<'a> System<'a> for Sys {
pos: pos.0,
explosion: Explosion {
effects: vec![
RadiusEffect::EntityEffect(Effect::Health(HealthChange {
amount: -500,
cause: HealthSource::Explosion { owner: *owner },
})),
RadiusEffect::Entity(
None,
Effect::Health(HealthChange {
amount: -500,
cause: HealthSource::Explosion { owner: *owner },
}),
),
RadiusEffect::TerrainDestruction(4.0),
],
radius: 12.0,
@ -74,10 +77,13 @@ impl<'a> System<'a> for Sys {
pos: pos.0,
explosion: Explosion {
effects: vec![
RadiusEffect::EntityEffect(Effect::Health(HealthChange {
amount: -50,
cause: HealthSource::Explosion { owner: *owner },
})),
RadiusEffect::Entity(
None,
Effect::Health(HealthChange {
amount: -50,
cause: HealthSource::Explosion { owner: *owner },
}),
),
RadiusEffect::TerrainDestruction(4.0),
],
radius: 12.0,