mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
parent
7517991d73
commit
e0cbbf52ed
@ -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)]
|
||||
|
@ -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,
|
||||
|
@ -31,7 +31,6 @@ pub enum ServerEvent {
|
||||
pos: Vec3<f32>,
|
||||
explosion: Explosion,
|
||||
owner: Option<Uid>,
|
||||
friendly_damage: bool,
|
||||
reagent: Option<Reagent>,
|
||||
},
|
||||
Damage {
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 =
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
})
|
||||
},
|
||||
|
@ -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,
|
||||
})
|
||||
},
|
||||
|
@ -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();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user