From b63a13bef1524ca3f1bf9500d11ce769c17c0bc4 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 30 Oct 2020 15:41:21 -0500 Subject: [PATCH] Changed explosions to take a vec of RadiusEffects. Changed Damage to a struct of DamageSource and value. Added interpolation function to damage. --- common/src/combat.rs | 57 +++-- common/src/comp/inventory/item/tool.rs | 66 ++++-- common/src/event.rs | 1 - common/src/explosion.rs | 13 +- common/src/lib.rs | 4 +- common/src/states/basic_beam.rs | 16 +- common/src/states/basic_melee.rs | 7 +- common/src/states/charged_melee.rs | 15 +- common/src/states/charged_ranged.rs | 16 +- common/src/states/combo_melee.rs | 10 +- common/src/states/dash_melee.rs | 16 +- common/src/states/leap_melee.rs | 7 +- common/src/states/shockwave.rs | 7 +- common/src/states/spin_melee.rs | 7 +- common/src/sys/beam.rs | 4 +- common/src/sys/projectile.rs | 2 - server/src/cmd.rs | 21 +- server/src/events/entity_manipulation.rs | 266 ++++++++++++----------- server/src/events/mod.rs | 3 +- server/src/sys/object.rs | 40 ++-- 20 files changed, 336 insertions(+), 242 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index bebda0aa11..ed11e2fb5e 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -24,17 +24,28 @@ impl Damages { pub fn get_damage(self, same_group: bool) -> Option { if same_group { self.group } else { 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 DamageSource { + Melee, + Healing, + Projectile, + Explosion, + Falling, + Shockwave, + Energy, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum Damage { - Melee(f32), - Healing(f32), - Projectile(f32), - Explosion(f32), - Falling(f32), - Shockwave(f32), - Energy(f32), +pub struct Damage { + pub source: DamageSource, + pub value: f32, } impl Damage { @@ -44,9 +55,9 @@ impl Damage { loadout: Option<&Loadout>, uid: Option, ) -> HealthChange { - match self { - Damage::Melee(damage) => { - let mut damage = damage; + let mut damage = self.value; + match self.source { + DamageSource::Melee => { // Critical hit let mut critdamage = 0.0; if rand::random() { @@ -70,8 +81,7 @@ impl Damage { cause: HealthSource::Attack { by: uid.unwrap() }, } }, - Damage::Projectile(damage) => { - let mut damage = damage; + DamageSource::Projectile => { // Critical hit if rand::random() { damage *= 1.2; @@ -89,8 +99,7 @@ impl Damage { cause: HealthSource::Projectile { owner: uid }, } }, - Damage::Explosion(damage) => { - let mut damage = damage; + DamageSource::Explosion => { // Block if block { damage *= 1.0 - BLOCK_EFFICIENCY @@ -104,8 +113,7 @@ impl Damage { cause: HealthSource::Explosion { owner: uid }, } }, - Damage::Shockwave(damage) => { - let mut damage = damage; + DamageSource::Shockwave => { // Armor let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; @@ -115,8 +123,7 @@ impl Damage { cause: HealthSource::Attack { by: uid.unwrap() }, } }, - Damage::Energy(damage) => { - let mut damage = damage; + DamageSource::Energy => { // Armor let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; @@ -126,12 +133,11 @@ impl Damage { cause: HealthSource::Energy { owner: uid }, } }, - Damage::Healing(heal) => HealthChange { - amount: heal as i32, + DamageSource::Healing => HealthChange { + amount: damage as i32, cause: HealthSource::Healing { by: uid }, }, - Damage::Falling(damage) => { - let mut damage = damage; + DamageSource::Falling => { // Armor let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); if (damage_reduction - 1.0).abs() < f32::EPSILON { @@ -144,6 +150,11 @@ impl Damage { }, } } + + pub fn interpolate_damage(&mut self, frac: f32, min: f32) { + let new_damage = min + frac * (self.value - min); + self.value = new_damage; + } } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 7f8b5499d8..9dac188ada 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -8,7 +8,7 @@ use crate::{ projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile, }, states::combo_melee, - Damage, Damages, Explosion, Knockback, + Damage, DamageSource, Damages, Explosion, Knockback, RadiusEffect, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -303,7 +303,10 @@ impl Tool { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ projectile::Effect::Damages(Damages::new( - Some(Damage::Projectile(40.0 * self.base_power())), + Some(Damage { + source: DamageSource::Projectile, + value: 40.0 * self.base_power(), + }), None, )), projectile::Effect::Knockback(Knockback::Away(10.0)), @@ -358,7 +361,10 @@ impl Tool { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ projectile::Effect::Damages(Damages::new( - Some(Damage::Projectile(40.0 * self.base_power())), + Some(Damage { + source: DamageSource::Projectile, + value: 40.0 * self.base_power(), + }), None, )), projectile::Effect::Knockback(Knockback::Away(10.0)), @@ -419,24 +425,34 @@ 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(), + }), + ))], radius: 3.0 + 2.5 * self.base_power(), - max_damage: (50.0 * self.base_power()) as u32, - min_damage: (20.0 * self.base_power()) as u32, - max_heal: (140.0 * self.base_power()) as u32, - min_heal: (50.0 * self.base_power()) as u32, - terrain_destruction_power: 0.0, energy_regen: 0, }), projectile::Effect::Vanish, ], 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(), + }), + ))], radius: 3.0 + 2.5 * self.base_power(), - max_damage: (50.0 * self.base_power()) as u32, - min_damage: (20.0 * self.base_power()) as u32, - max_heal: (140.0 * self.base_power()) as u32, - min_heal: (50.0 * self.base_power()) as u32, - terrain_destruction_power: 0.0, energy_regen: 0, }), projectile::Effect::Vanish, @@ -462,24 +478,28 @@ impl Tool { projectile: Projectile { hit_solid: vec![ projectile::Effect::Explode(Explosion { + effects: vec![RadiusEffect::Damages(Damages::new( + Some(Damage { + source: DamageSource::Explosion, + value: 100.0 * self.base_power(), + }), + None, + ))], radius: 5.0, - max_damage: (100.0 * self.base_power()) as u32, - min_damage: 0, - max_heal: 0, - min_heal: 0, - terrain_destruction_power: 0.0, energy_regen: 50, }), projectile::Effect::Vanish, ], hit_entity: vec![ projectile::Effect::Explode(Explosion { + effects: vec![RadiusEffect::Damages(Damages::new( + Some(Damage { + source: DamageSource::Explosion, + value: 100.0 * self.base_power(), + }), + None, + ))], radius: 5.0, - max_damage: (100.0 * self.base_power()) as u32, - min_damage: 0, - max_heal: 0, - min_heal: 0, - terrain_destruction_power: 0.0, energy_regen: 50, }), projectile::Effect::Vanish, diff --git a/common/src/event.rs b/common/src/event.rs index a51620c961..9dc0d725de 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -31,7 +31,6 @@ pub enum ServerEvent { pos: Vec3, explosion: Explosion, owner: Option, - friendly_damage: bool, reagent: Option, }, Damage { diff --git a/common/src/explosion.rs b/common/src/explosion.rs index 2779f50534..941d534c59 100644 --- a/common/src/explosion.rs +++ b/common/src/explosion.rs @@ -1,12 +1,15 @@ +use crate::combat::Damages; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Explosion { + pub effects: Vec, pub radius: f32, - pub max_damage: u32, - pub min_damage: u32, - pub max_heal: u32, - pub min_heal: u32, - pub terrain_destruction_power: f32, pub energy_regen: u32, } + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum RadiusEffect { + Damages(Damages), + TerrainDestruction(f32), +} diff --git a/common/src/lib.rs b/common/src/lib.rs index b35122fa67..1959d85135 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -53,6 +53,6 @@ pub mod util; pub mod vol; pub mod volumes; -pub use combat::{Damage, Damages, Knockback}; -pub use explosion::Explosion; +pub use combat::{Damage, DamageSource, Damages, Knockback}; +pub use explosion::{Explosion, RadiusEffect}; pub use loadout_builder::LoadoutBuilder; diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 4a387ba7da..12ff5fd381 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -4,7 +4,7 @@ use crate::{ states::utils::*, sync::Uid, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, + Damage, DamageSource, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -107,12 +107,14 @@ 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 = 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 damage = Damage { + source: DamageSource::Energy, + value: self.static_data.base_dps as f32 / self.static_data.tick_rate, + }; + let heal = Damage { + source: DamageSource::Healing, + value: 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 = diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 228a10a204..ca4e002cdd 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, Knockback, + Damage, DamageSource, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -77,7 +77,10 @@ impl CharacterBehavior for Data { // Hit attempt data.updater.insert(data.entity, Attacking { damages: Damages::new( - Some(Damage::Melee(self.static_data.base_damage as f32)), + Some(Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }), None, ), range: self.static_data.range, diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index e0255e89a4..deec9de03c 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::{StageSection, *}, sys::character_behavior::*, - Damage, Damages, Knockback, + Damage, DamageSource, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -109,16 +109,21 @@ impl CharacterBehavior for Data { }, StageSection::Swing => { if !self.exhausted { - 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 mut damage = Damage { + source: DamageSource::Melee, + value: self.static_data.max_damage as f32, + }; + damage.interpolate_damage( + self.charge_amount, + self.static_data.initial_damage as f32, + ); 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 { - damages: Damages::new(Some(Damage::Melee(damage)), None), + damages: Damages::new(Some(damage), None), range: self.static_data.range, max_angle: self.static_data.max_angle.to_radians(), applied: false, diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 32d1796527..e6141cfe44 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -7,7 +7,7 @@ use crate::{ event::ServerEvent, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, Knockback, + Damage, DamageSource, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -84,10 +84,11 @@ impl CharacterBehavior for Data { let charge_frac = (self.timer.as_secs_f32() / self.static_data.charge_duration.as_secs_f32()) .min(1.0); - let damage = self.static_data.initial_damage as f32 - + (charge_frac - * (self.static_data.max_damage - self.static_data.initial_damage) - as f32); + let mut damage = Damage { + source: DamageSource::Projectile, + value: self.static_data.max_damage as f32, + }; + damage.interpolate_damage(charge_frac, self.static_data.initial_damage as f32); let knockback = self.static_data.initial_knockback as f32 + (charge_frac * (self.static_data.max_knockback - self.static_data.initial_knockback) @@ -96,10 +97,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::Projectile(damage)), - None, - )), + projectile::Effect::Damages(Damages::new(Some(damage), None)), projectile::Effect::Knockback(Knockback::Away(knockback)), projectile::Effect::Vanish, projectile::Effect::Buff { diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 285804e278..4fe358c1b4 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, Knockback, + Damage, DamageSource, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -127,7 +127,13 @@ impl CharacterBehavior for Data { * self.static_data.stage_data[stage_index].damage_increase, ); data.updater.insert(data.entity, Attacking { - damages: Damages::new(Some(Damage::Melee(damage as f32)), None), + damages: Damages::new( + Some(Damage { + source: DamageSource::Melee, + value: 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, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 8a99b80bd9..3da10c386c 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, Knockback, + Damage, DamageSource, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -122,16 +122,20 @@ impl CharacterBehavior for Data { let charge_frac = (self.timer.as_secs_f32() / self.static_data.charge_duration.as_secs_f32()) .min(1.0); - let damage = (self.static_data.max_damage as f32 - - self.static_data.base_damage as f32) - * charge_frac - + self.static_data.base_damage as f32; + let mut damage = Damage { + source: DamageSource::Melee, + value: self.static_data.max_damage as f32, + }; + damage.interpolate_damage( + charge_frac, + self.static_data.base_damage as f32, + ); let knockback = (self.static_data.max_knockback - self.static_data.base_knockback) * charge_frac + self.static_data.base_knockback; data.updater.insert(data.entity, Attacking { - damages: Damages::new(Some(Damage::Melee(damage)), None), + damages: Damages::new(Some(damage), None), range: self.static_data.range, max_angle: self.static_data.angle.to_radians(), applied: false, diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 0739761281..886375c76e 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, StateUpdate}, states::utils::{StageSection, *}, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, Knockback, + Damage, DamageSource, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -134,7 +134,10 @@ impl CharacterBehavior for Data { // Hit attempt, when animation plays data.updater.insert(data.entity, Attacking { damages: Damages::new( - Some(Damage::Melee(self.static_data.base_damage as f32)), + Some(Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }), None, ), range: self.static_data.range, diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index de0cbf3ab5..0141f0112a 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -3,7 +3,7 @@ use crate::{ event::ServerEvent, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, Knockback, + Damage, DamageSource, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -71,7 +71,10 @@ impl CharacterBehavior for Data { speed: self.static_data.shockwave_speed, duration: self.static_data.shockwave_duration, damages: Damages::new( - Some(Damage::Shockwave(self.static_data.damage as f32)), + Some(Damage { + source: DamageSource::Shockwave, + value: self.static_data.damage as f32, + }), None, ), knockback: self.static_data.knockback, diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 14c4bec054..746e2bf58a 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -5,7 +5,7 @@ use crate::{ character_behavior::{CharacterBehavior, JoinData}, phys::GRAVITY, }, - Damage, Damages, Knockback, + Damage, DamageSource, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -105,7 +105,10 @@ impl CharacterBehavior for Data { // Hit attempt data.updater.insert(data.entity, Attacking { damages: Damages::new( - Some(Damage::Melee(self.static_data.base_damage as f32)), + Some(Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }), None, ), range: self.static_data.range, diff --git a/common/src/sys/beam.rs b/common/src/sys/beam.rs index 8b6807b1dc..3cf769a9a5 100644 --- a/common/src/sys/beam.rs +++ b/common/src/sys/beam.rs @@ -6,7 +6,7 @@ use crate::{ event::{EventBus, ServerEvent}, state::{DeltaTime, Time}, sync::{Uid, UidAllocator}, - Damage, + DamageSource, }; use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage}; use std::time::Duration; @@ -180,7 +180,7 @@ impl<'a> System<'a> for Sys { let change = damage.modify_damage(block, loadouts.get(b), beam_segment.owner); - if !matches!(damage, Damage::Healing(_)) { + if !matches!(damage.source, DamageSource::Healing) { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, change, diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index eae08e8055..a8dce0663e 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -142,7 +142,6 @@ impl<'a> System<'a> for Sys { pos: pos.0, explosion: e, owner: projectile.owner, - friendly_damage: false, reagent: None, }) }, @@ -187,7 +186,6 @@ impl<'a> System<'a> for Sys { pos: pos.0, explosion: e, owner: projectile.owner, - friendly_damage: false, reagent: None, }) }, diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 6050409002..2db160010e 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -18,7 +18,7 @@ use common::{ terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize}, util::Dir, vol::RectVolSize, - Explosion, LoadoutBuilder, + Damage, DamageSource, Damages, Explosion, LoadoutBuilder, RadiusEffect, }; use rand::Rng; use specs::{Builder, Entity as EcsEntity, Join, WorldExt}; @@ -1153,16 +1153,23 @@ fn handle_explosion( .emit_now(ServerEvent::Explosion { pos: pos.0, explosion: Explosion { + effects: vec![ + RadiusEffect::Damages(Damages::new( + Some(Damage { + source: DamageSource::Explosion, + value: 100.0 * power, + }), + Some(Damage { + source: DamageSource::Explosion, + value: 100.0 * power, + }), + )), + RadiusEffect::TerrainDestruction(power), + ], radius: 3.0 * power, - max_damage: (100.0 * power) as u32, - min_damage: 0, - max_heal: 0, - min_heal: 0, - terrain_destruction_power: power, energy_regen: 0, }, owner: ecs.read_storage::().get(target).copied(), - friendly_damage: true, reagent: None, }) }, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 48ae6b7563..5c6c8d0ab9 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -18,7 +18,7 @@ use common::{ sys::melee::BLOCK_ANGLE, terrain::{Block, TerrainGrid}, vol::ReadVol, - Damage, Explosion, + Damage, DamageSource, Explosion, RadiusEffect, }; use comp::item::Reagent; use rand::prelude::*; @@ -454,7 +454,10 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) if vel.z <= -30.0 { if let Some(stats) = state.ecs().write_storage::().get_mut(entity) { let falldmg = (vel.z.powi(2) / 20.0 - 40.0) * 10.0; - let damage = Damage::Falling(falldmg); + let damage = Damage { + source: DamageSource::Falling, + value: falldmg, + }; let loadouts = state.ecs().read_storage::(); let change = damage.modify_damage(false, loadouts.get(entity), None); stats.health.change_by(change); @@ -506,26 +509,35 @@ pub fn handle_explosion( pos: Vec3, explosion: Explosion, owner: Option, - friendly_damage: bool, reagent: Option, ) { // Go through all other entities let ecs = &server.state.ecs(); - let outcome_power = if explosion.max_heal > explosion.max_damage { - (-explosion.terrain_destruction_power).min(explosion.max_heal as f32 / -100.0) - } else { - explosion - .terrain_destruction_power - .max(explosion.max_damage as f32 / 100.0) - }; // Add an outcome + // Uses radius as outcome power, makes negative if explosion has healing effect + #[allow(clippy::blocks_in_if_conditions)] + let outcome_power = explosion.radius + * if explosion.effects.iter().any(|e| { + if let RadiusEffect::Damages(d) = e { + d.contains_damage(DamageSource::Healing) + } else { + false + } + }) { + -1.0 + } else { + 1.0 + }; ecs.write_resource::>() .push(Outcome::Explosion { pos, power: outcome_power, radius: explosion.radius, - is_attack: explosion.max_heal > 0 || explosion.max_damage > 0, + is_attack: explosion + .effects + .iter() + .any(|e| matches!(e, RadiusEffect::Damages(_))), reagent, }); let owner_entity = owner.and_then(|uid| { @@ -534,129 +546,133 @@ pub fn handle_explosion( }); let groups = ecs.read_storage::(); - for (entity_b, pos_b, ori_b, character_b, stats_b, loadout_b) in ( - &ecs.entities(), - &ecs.read_storage::(), - &ecs.read_storage::(), - ecs.read_storage::().maybe(), - &mut ecs.write_storage::(), - ecs.read_storage::().maybe(), - ) - .join() - { - let distance_squared = pos.distance_squared(pos_b.0); - // Check if it is a hit - if !stats_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; - } - } - // Don't heal if outside group - // Don't damage in the same group - let is_damage = (friendly_damage || !same_group) && explosion.max_damage > 0; - let is_heal = same_group && explosion.max_heal > 0 && !friendly_damage; - if !is_heal && !is_damage { - continue; - } - - let strength = 1.0 - distance_squared / explosion.radius.powi(2); - let damage = if is_heal { - Damage::Healing( - explosion.min_heal as f32 - + (explosion.max_heal - explosion.min_heal) as f32 * strength, + for effect in explosion.effects { + match effect { + RadiusEffect::Damages(damages) => { + for (entity_b, pos_b, ori_b, character_b, stats_b, loadout_b) in ( + &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + &mut ecs.write_storage::(), + ecs.read_storage::().maybe(), ) - } else { - Damage::Explosion( - explosion.min_damage as f32 - + (explosion.max_damage - explosion.min_damage) as f32 * strength, - ) - }; + .join() + { + let distance_squared = pos.distance_squared(pos_b.0); + // Check if it is a hit + if !stats_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 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 mut damage = if let Some(damage) = damages.get_damage(same_group) { + damage + } else { + continue; + }; - let change = damage.modify_damage(block, loadout_b, owner); + let strength = 1.0 - distance_squared / explosion.radius.powi(2); + damage.interpolate_damage(strength, 0.0); - if change.amount != 0 { - stats_b.health.change_by(change); - if let Some(owner) = owner_entity { - if let Some(energy) = ecs.write_storage::().get_mut(owner) { - energy - .change_by(explosion.energy_regen as i32, comp::EnergySource::HitEnemy); + 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 { + stats_b.health.change_by(change); + if let Some(owner) = owner_entity { + if let Some(energy) = + ecs.write_storage::().get_mut(owner) + { + energy.change_by( + explosion.energy_regen as i32, + comp::EnergySource::HitEnemy, + ); + } + } + } } } - } - } - } + }, + RadiusEffect::TerrainDestruction(power) => { + const RAYS: usize = 500; - const RAYS: usize = 500; + // Color terrain + let mut touched_blocks = Vec::new(); + let color_range = power * 2.7; + for _ in 0..RAYS { + let dir = Vec3::new( + rand::random::() - 0.5, + rand::random::() - 0.5, + rand::random::() - 0.5, + ) + .normalized(); - // Color terrain - let mut touched_blocks = Vec::new(); - let color_range = explosion.terrain_destruction_power * 2.7; - for _ in 0..RAYS { - let dir = Vec3::new( - rand::random::() - 0.5, - rand::random::() - 0.5, - rand::random::() - 0.5, - ) - .normalized(); - - let _ = ecs - .read_resource::() - .ray(pos, pos + dir * color_range) - // TODO: Faster RNG - .until(|_| rand::random::() < 0.05) - .for_each(|_: &Block, pos| touched_blocks.push(pos)) - .cast(); - } - - let terrain = ecs.read_resource::(); - let mut block_change = ecs.write_resource::(); - for block_pos in touched_blocks { - if let Ok(block) = terrain.get(block_pos) { - let diff2 = block_pos.map(|b| b as f32).distance_squared(pos); - let fade = (1.0 - diff2 / color_range.powi(2)).max(0.0); - if let Some(mut color) = block.get_color() { - let r = color[0] as f32 + (fade * (color[0] as f32 * 0.5 - color[0] as f32)); - let g = color[1] as f32 + (fade * (color[1] as f32 * 0.3 - color[1] as f32)); - let b = color[2] as f32 + (fade * (color[2] as f32 * 0.3 - color[2] as f32)); - color[0] = r as u8; - color[1] = g as u8; - color[2] = b as u8; - block_change.set(block_pos, Block::new(block.kind(), color)); - } - } - } - - // Destroy terrain - for _ in 0..RAYS { - let dir = Vec3::new( - rand::random::() - 0.5, - rand::random::() - 0.5, - rand::random::() - 0.15, - ) - .normalized(); - - let terrain = ecs.read_resource::(); - let _ = terrain - .ray(pos, pos + dir * explosion.terrain_destruction_power) - // TODO: Faster RNG - .until(|block| block.is_liquid() || rand::random::() < 0.05) - .for_each(|block: &Block, pos| { - if block.is_explodable() { - block_change.set(pos, block.into_vacant()); + let _ = ecs + .read_resource::() + .ray(pos, pos + dir * color_range) + // TODO: Faster RNG + .until(|_| rand::random::() < 0.05) + .for_each(|_: &Block, pos| touched_blocks.push(pos)) + .cast(); } - }) - .cast(); + + let terrain = ecs.read_resource::(); + let mut block_change = ecs.write_resource::(); + for block_pos in touched_blocks { + if let Ok(block) = terrain.get(block_pos) { + let diff2 = block_pos.map(|b| b as f32).distance_squared(pos); + let fade = (1.0 - diff2 / color_range.powi(2)).max(0.0); + if let Some(mut color) = block.get_color() { + let r = color[0] as f32 + + (fade * (color[0] as f32 * 0.5 - color[0] as f32)); + let g = color[1] as f32 + + (fade * (color[1] as f32 * 0.3 - color[1] as f32)); + let b = color[2] as f32 + + (fade * (color[2] as f32 * 0.3 - color[2] as f32)); + color[0] = r as u8; + color[1] = g as u8; + color[2] = b as u8; + block_change.set(block_pos, Block::new(block.kind(), color)); + } + } + } + + // Destroy terrain + for _ in 0..RAYS { + let dir = Vec3::new( + rand::random::() - 0.5, + rand::random::() - 0.5, + rand::random::() - 0.15, + ) + .normalized(); + + let terrain = ecs.read_resource::(); + let _ = terrain + .ray(pos, pos + dir * power) + // TODO: Faster RNG + .until(|block| block.is_liquid() || rand::random::() < 0.05) + .for_each(|block: &Block, pos| { + if block.is_explodable() { + block_change.set(pos, block.into_vacant()); + } + }) + .cast(); + } + }, + } } } diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 651b49a350..9a9f8cd6ba 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -58,9 +58,8 @@ impl Server { pos, explosion, owner, - friendly_damage, reagent, - } => handle_explosion(&self, pos, explosion, owner, friendly_damage, reagent), + } => handle_explosion(&self, pos, explosion, owner, reagent), ServerEvent::Shoot { entity, dir, diff --git a/server/src/sys/object.rs b/server/src/sys/object.rs index f5ab4be8f3..110895e009 100644 --- a/server/src/sys/object.rs +++ b/server/src/sys/object.rs @@ -3,7 +3,7 @@ use common::{ event::{EventBus, ServerEvent}, span, state::DeltaTime, - Explosion, + Damage, DamageSource, Damages, Explosion, RadiusEffect, }; use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; @@ -48,16 +48,23 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::Explosion { pos: pos.0, explosion: Explosion { + effects: vec![ + RadiusEffect::Damages(Damages::new( + Some(Damage { + source: DamageSource::Explosion, + value: 500.0, + }), + Some(Damage { + source: DamageSource::Explosion, + value: 500.0, + }), + )), + RadiusEffect::TerrainDestruction(4.0), + ], radius: 12.0, - max_damage: 500, - min_damage: 100, - max_heal: 0, - min_heal: 0, - terrain_destruction_power: 4.0, energy_regen: 0, }, owner: *owner, - friendly_damage: true, reagent: None, }); } @@ -71,16 +78,23 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::Explosion { pos: pos.0, explosion: Explosion { + effects: vec![ + RadiusEffect::Damages(Damages::new( + Some(Damage { + source: DamageSource::Explosion, + value: 50.0, + }), + Some(Damage { + source: DamageSource::Explosion, + value: 50.0, + }), + )), + RadiusEffect::TerrainDestruction(4.0), + ], radius: 12.0, - max_damage: 50, - min_damage: 10, - max_heal: 0, - min_heal: 0, - terrain_destruction_power: 4.0, energy_regen: 0, }, owner: *owner, - friendly_damage: true, reagent: Some(*reagent), }); }