Changed explosions to take a vec of RadiusEffects. Changed Damage to a struct of DamageSource and value. Added interpolation function to damage.

This commit is contained in:
Sam 2020-10-30 15:41:21 -05:00
parent 7517991d73
commit e0cbbf52ed
20 changed files with 336 additions and 242 deletions

View File

@ -24,17 +24,28 @@ impl Damages {
pub fn get_damage(self, same_group: bool) -> Option<Damage> {
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<Uid>,
) -> 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)]

View File

@ -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,

View File

@ -31,7 +31,6 @@ pub enum ServerEvent {
pos: Vec3<f32>,
explosion: Explosion,
owner: Option<Uid>,
friendly_damage: bool,
reagent: Option<Reagent>,
},
Damage {

View File

@ -1,12 +1,15 @@
use crate::combat::Damages;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Explosion {
pub effects: Vec<RadiusEffect>,
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),
}

View File

@ -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;

View File

@ -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 =

View File

@ -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,

View File

@ -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,

View File

@ -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 {

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,
})
},

View File

@ -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::<Uid>().get(target).copied(),
friendly_damage: true,
reagent: None,
})
},

View File

@ -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<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 damage = Damage::Falling(falldmg);
let damage = Damage {
source: DamageSource::Falling,
value: falldmg,
};
let loadouts = state.ecs().read_storage::<comp::Loadout>();
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<f32>,
explosion: Explosion,
owner: Option<Uid>,
friendly_damage: bool,
reagent: Option<Reagent>,
) {
// 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::<Vec<Outcome>>()
.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::<comp::Group>();
for (entity_b, pos_b, ori_b, character_b, stats_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::Stats>(),
ecs.read_storage::<comp::Loadout>().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::<comp::Pos>(),
&ecs.read_storage::<comp::Ori>(),
ecs.read_storage::<comp::CharacterState>().maybe(),
&mut ecs.write_storage::<comp::Stats>(),
ecs.read_storage::<comp::Loadout>().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::<comp::Energy>().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::<comp::Energy>().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::<f32>() - 0.5,
rand::random::<f32>() - 0.5,
rand::random::<f32>() - 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::<f32>() - 0.5,
rand::random::<f32>() - 0.5,
rand::random::<f32>() - 0.5,
)
.normalized();
let _ = ecs
.read_resource::<TerrainGrid>()
.ray(pos, pos + dir * color_range)
// TODO: Faster RNG
.until(|_| rand::random::<f32>() < 0.05)
.for_each(|_: &Block, pos| touched_blocks.push(pos))
.cast();
}
let terrain = ecs.read_resource::<TerrainGrid>();
let mut block_change = ecs.write_resource::<BlockChange>();
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::<f32>() - 0.5,
rand::random::<f32>() - 0.5,
rand::random::<f32>() - 0.15,
)
.normalized();
let terrain = ecs.read_resource::<TerrainGrid>();
let _ = terrain
.ray(pos, pos + dir * explosion.terrain_destruction_power)
// TODO: Faster RNG
.until(|block| block.is_liquid() || rand::random::<f32>() < 0.05)
.for_each(|block: &Block, pos| {
if block.is_explodable() {
block_change.set(pos, block.into_vacant());
let _ = ecs
.read_resource::<TerrainGrid>()
.ray(pos, pos + dir * color_range)
// TODO: Faster RNG
.until(|_| rand::random::<f32>() < 0.05)
.for_each(|_: &Block, pos| touched_blocks.push(pos))
.cast();
}
})
.cast();
let terrain = ecs.read_resource::<TerrainGrid>();
let mut block_change = ecs.write_resource::<BlockChange>();
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::<f32>() - 0.5,
rand::random::<f32>() - 0.5,
rand::random::<f32>() - 0.15,
)
.normalized();
let terrain = ecs.read_resource::<TerrainGrid>();
let _ = terrain
.ray(pos, pos + dir * power)
// TODO: Faster RNG
.until(|block| block.is_liquid() || rand::random::<f32>() < 0.05)
.for_each(|block: &Block, pos| {
if block.is_explodable() {
block_change.set(pos, block.into_vacant());
}
})
.cast();
}
},
}
}
}

View File

@ -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,

View File

@ -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),
});
}