Slashing damage now decreases target's energy if available, and if target has no remaining energy will do additional damage.

Piercing damage now ignores an amount of protection equal to the piercing damage value.
Crushing damage now does poise damage equal to the amount of mitigated damage.
When poise damage is dealt while in a poise state, poise damage is instead converted to damage.
This commit is contained in:
Sam 2022-01-07 00:30:28 -05:00
parent c6fc42be82
commit d1e1de3b14
17 changed files with 377 additions and 200 deletions

View File

@ -1,56 +1,69 @@
ComboMelee(
stage_data: [
(
stage: 1,
base_damage: 11.0,
base_poise_damage: 12,
damage_increase: 1.0,
poise_damage_increase: 0,
knockback: 5.0,
range: 3.5,
angle: 50.0,
base_buildup_duration: 0.15,
base_swing_duration: 0.075,
hit_timing: 0.6,
base_recover_duration: 0.35,
forward_movement: 0.5,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
(
stage: 2,
base_damage: 13.0,
base_poise_damage: 20,
damage_increase: 1.5,
poise_damage_increase: 0,
knockback: 6.0,
range: 3.5,
angle: 30.0,
base_buildup_duration: 0.2,
base_swing_duration: 0.1,
hit_timing: 0.6,
base_recover_duration: 0.35,
forward_movement: 0.25,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
],
initial_energy_gain: 2.5,
max_energy_gain: 17.5,
energy_increase: 3.0,
speed_increase: 0.1,
max_speed_increase: 0.6,
scales_from_combo: 2,
is_interruptible: false,
ori_modifier: 1.0,
// ComboMelee(
// stage_data: [
// (
// stage: 1,
// base_damage: 11.0,
// base_poise_damage: 12,
// damage_increase: 1.0,
// poise_damage_increase: 0,
// knockback: 5.0,
// range: 3.5,
// angle: 50.0,
// base_buildup_duration: 0.15,
// base_swing_duration: 0.075,
// hit_timing: 0.6,
// base_recover_duration: 0.35,
// forward_movement: 0.5,
// damage_kind: Slashing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// (
// stage: 2,
// base_damage: 13.0,
// base_poise_damage: 20,
// damage_increase: 1.5,
// poise_damage_increase: 0,
// knockback: 6.0,
// range: 3.5,
// angle: 30.0,
// base_buildup_duration: 0.2,
// base_swing_duration: 0.1,
// hit_timing: 0.6,
// base_recover_duration: 0.35,
// forward_movement: 0.25,
// damage_kind: Slashing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// ],
// initial_energy_gain: 2.5,
// max_energy_gain: 17.5,
// energy_increase: 3.0,
// speed_increase: 0.1,
// max_speed_increase: 0.6,
// scales_from_combo: 2,
// is_interruptible: false,
// ori_modifier: 1.0,
// )
BasicMelee(
energy_cost: 0,
buildup_duration: 0.1,
swing_duration: 0.05,
recover_duration: 0.1,
base_damage: 10.0,
base_poise_damage: 0,
knockback: ( strength: 0.0, direction: Away),
range: 5.0,
max_angle: 45.0,
damage_effect: None,
damage_kind: Slashing,
)

View File

@ -1,26 +1,39 @@
ComboMelee(
stage_data: [(
stage: 1,
base_damage: 15.0,
damage_increase: 1.0,
base_poise_damage: 20,
poise_damage_increase: 0,
knockback: 3.5,
range: 4.5,
angle: 50.0,
base_buildup_duration: 0.2,
base_swing_duration: 0.1,
hit_timing: 0.5,
base_recover_duration: 0.45,
forward_movement: 0.0,
damage_kind: Crushing,
)],
initial_energy_gain: 5.0,
max_energy_gain: 15.0,
energy_increase: 5.0,
speed_increase: 0.1,
max_speed_increase: 0.4,
scales_from_combo: 2,
is_interruptible: false,
ori_modifier: 1.0,
)
// ComboMelee(
// stage_data: [(
// stage: 1,
// base_damage: 15.0,
// damage_increase: 1.0,
// base_poise_damage: 20,
// poise_damage_increase: 0,
// knockback: 3.5,
// range: 4.5,
// angle: 50.0,
// base_buildup_duration: 0.2,
// base_swing_duration: 0.1,
// hit_timing: 0.5,
// base_recover_duration: 0.45,
// forward_movement: 0.0,
// damage_kind: Crushing,
// )],
// initial_energy_gain: 5.0,
// max_energy_gain: 15.0,
// energy_increase: 5.0,
// speed_increase: 0.1,
// max_speed_increase: 0.4,
// scales_from_combo: 2,
// is_interruptible: false,
// ori_modifier: 1.0,
// )
BasicMelee(
energy_cost: 0,
buildup_duration: 0.1,
swing_duration: 0.05,
recover_duration: 0.1,
base_damage: 10.0,
base_poise_damage: 0,
knockback: ( strength: 0.0, direction: Away),
range: 5.0,
max_angle: 45.0,
damage_effect: None,
damage_kind: Crushing,
)

View File

@ -1,78 +1,91 @@
ComboMelee(
stage_data: [
(
stage: 1,
base_damage: 10.0,
damage_increase: 1.0,
base_poise_damage: 10,
poise_damage_increase: 0,
knockback: 0.0,
range: 4.0,
angle: 30.0,
base_buildup_duration: 0.1,
base_swing_duration: 0.075,
hit_timing: 0.5,
base_recover_duration: 0.15,
forward_movement: 0.5,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
(
stage: 2,
base_damage: 8.0,
damage_increase: 1.5,
base_poise_damage: 13,
poise_damage_increase: 0,
knockback: 2.0,
range: 3.5,
angle: 40.0,
base_buildup_duration: 0.1,
base_swing_duration: 0.1,
hit_timing: 0.5,
base_recover_duration: 0.3,
forward_movement: 0.0,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
(
stage: 3,
base_damage: 13,
damage_increase: 2,
base_poise_damage: 15,
poise_damage_increase: 0,
knockback: 2.0,
range: 6.0,
angle: 10.0,
base_buildup_duration: 0.15,
base_swing_duration: 0.1,
hit_timing: 0.2,
base_recover_duration: 0.35,
forward_movement: 1.2,
damage_kind: Piercing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
],
initial_energy_gain: 0,
max_energy_gain: 20.0,
energy_increase: 2.5,
speed_increase: 0.1,
max_speed_increase: 0.8,
scales_from_combo: 2,
is_interruptible: true,
ori_modifier: 1.0,
// ComboMelee(
// stage_data: [
// (
// stage: 1,
// base_damage: 10.0,
// damage_increase: 1.0,
// base_poise_damage: 10,
// poise_damage_increase: 0,
// knockback: 0.0,
// range: 4.0,
// angle: 30.0,
// base_buildup_duration: 0.1,
// base_swing_duration: 0.075,
// hit_timing: 0.5,
// base_recover_duration: 0.15,
// forward_movement: 0.5,
// damage_kind: Slashing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// (
// stage: 2,
// base_damage: 8.0,
// damage_increase: 1.5,
// base_poise_damage: 13,
// poise_damage_increase: 0,
// knockback: 2.0,
// range: 3.5,
// angle: 40.0,
// base_buildup_duration: 0.1,
// base_swing_duration: 0.1,
// hit_timing: 0.5,
// base_recover_duration: 0.3,
// forward_movement: 0.0,
// damage_kind: Slashing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// (
// stage: 3,
// base_damage: 13,
// damage_increase: 2,
// base_poise_damage: 15,
// poise_damage_increase: 0,
// knockback: 2.0,
// range: 6.0,
// angle: 10.0,
// base_buildup_duration: 0.15,
// base_swing_duration: 0.1,
// hit_timing: 0.2,
// base_recover_duration: 0.35,
// forward_movement: 1.2,
// damage_kind: Piercing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// ],
// initial_energy_gain: 0,
// max_energy_gain: 20.0,
// energy_increase: 2.5,
// speed_increase: 0.1,
// max_speed_increase: 0.8,
// scales_from_combo: 2,
// is_interruptible: true,
// ori_modifier: 1.0,
// )
BasicMelee(
energy_cost: 0,
buildup_duration: 0.1,
swing_duration: 0.05,
recover_duration: 0.1,
base_damage: 10.0,
base_poise_damage: 0,
knockback: ( strength: 0.0, direction: Away),
range: 5.0,
max_angle: 45.0,
damage_effect: None,
damage_kind: Piercing,
)

View File

@ -12,7 +12,7 @@ use crate::{
},
skillset::SkillGroupKind,
Alignment, Body, CharacterState, Combo, Energy, Health, HealthChange, Inventory, Ori,
Player, Poise, SkillSet, Stats,
Player, Poise, PoiseChange, SkillSet, Stats,
},
event::ServerEvent,
outcome::Outcome,
@ -70,6 +70,7 @@ pub struct TargetInfo<'a> {
pub pos: Vec3<f32>,
pub ori: Option<&'a Ori>,
pub char_state: Option<&'a CharacterState>,
pub energy: Option<&'a Energy>,
}
#[derive(Clone, Copy)]
@ -135,12 +136,12 @@ impl Attack {
target: &TargetInfo,
source: AttackSource,
dir: Dir,
kind: DamageKind,
damage: Damage,
mut emit: impl FnMut(ServerEvent),
mut emit_outcome: impl FnMut(Outcome),
) -> f32 {
let damage_reduction =
Damage::compute_damage_reduction(target.inventory, target.stats, Some(kind));
Damage::compute_damage_reduction(Some(damage), target.inventory, target.stats);
let block_reduction = match source {
AttackSource::Melee => {
if let (Some(CharacterState::BasicBlock(data)), Some(ori)) =
@ -223,7 +224,7 @@ impl Attack {
&target,
attack_source,
dir,
damage.damage.kind,
damage.damage,
&mut emit,
&mut emit_outcome,
);
@ -243,6 +244,57 @@ impl Attack {
entity: target.entity,
change,
});
match damage.damage.kind {
DamageKind::Slashing => {
// For slashing damage, reduce target energy by some fraction of applied
// damage. When target would lose more energy than they have, deal an
// equivalent amount of damage
if let Some(target_energy) = target.energy {
let energy_change = applied_damage * SLASHING_ENERGY_FRACTION;
if energy_change > target_energy.current() {
let health_change = HealthChange {
amount: -(energy_change - target_energy.current()),
by: attacker.map(|x| x.into()),
cause: Some(damage.damage.source),
time,
};
emit(ServerEvent::HealthChange {
entity: target.entity,
change: health_change,
});
}
emit(ServerEvent::EnergyChange {
entity: target.entity,
change: -energy_change,
});
}
},
DamageKind::Crushing => {
// For crushing damage, reduce target poise by some fraction of the amount
// of damage that was reduced by target's protection
// Damage reduction should never equal 1 here as otherwise the check above
// that health change amount is greater than 0 would fail.
let reduced_damage =
applied_damage * damage_reduction / (1.0 - damage_reduction);
let poise = reduced_damage * CRUSHING_POISE_FRACTION;
let change = Poise::apply_poise_reduction(poise, target.inventory);
let poise_change = PoiseChange {
amount: change,
impulse: *dir,
by: attacker.map(|x| x.into()),
cause: Some(damage.damage.source),
};
if change.abs() > Poise::POISE_EPSILON {
emit(ServerEvent::PoiseChange {
entity: target.entity,
change: poise_change,
});
}
},
// Piercing damage ignores some penetration, and is handled when damage
// reduction is computed Energy is a placeholder damage type
DamageKind::Piercing | DamageKind::Energy => {},
}
for effect in damage.effects.iter() {
match effect {
CombatEffect::Knockback(kb) => {
@ -297,10 +349,15 @@ impl Attack {
let change = -Poise::apply_poise_reduction(*p, target.inventory)
* strength_modifier;
if change.abs() > Poise::POISE_EPSILON {
let poise_change = PoiseChange {
amount: change,
impulse: *dir,
by: attacker.map(|x| x.into()),
cause: Some(damage.damage.source),
};
emit(ServerEvent::PoiseChange {
entity: target.entity,
change,
kb_dir: *dir,
change: poise_change,
});
}
},
@ -434,10 +491,15 @@ impl Attack {
let change =
-Poise::apply_poise_reduction(p, target.inventory) * strength_modifier;
if change.abs() > Poise::POISE_EPSILON {
let poise_change = PoiseChange {
amount: change,
impulse: *dir,
by: attacker.map(|x| x.into()),
cause: Some(attack_source.into()),
};
emit(ServerEvent::PoiseChange {
entity: target.entity,
change,
kb_dir: *dir,
change: poise_change,
});
}
},
@ -649,20 +711,37 @@ pub enum DamageSource {
Other,
}
impl From<AttackSource> for DamageSource {
fn from(attack: AttackSource) -> Self {
match attack {
AttackSource::Melee => DamageSource::Melee,
AttackSource::Projectile => DamageSource::Projectile,
AttackSource::Explosion => DamageSource::Explosion,
AttackSource::Shockwave => DamageSource::Shockwave,
AttackSource::Beam => DamageSource::Energy,
}
}
}
/// DamageKind for the purpose of differentiating damage reduction
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum DamageKind {
/// Arrows/Sword dash
/// Bypasses some protection from armor
Piercing,
/// Swords/axes
/// Reduces energy of target, dealing additional damage when target energy
/// is 0
Slashing,
/// Hammers
/// Deals additional poise damage the more armored the target is
Crushing,
/// Staves/sceptres (TODO: differentiate further once there are more magic
/// weapons)
/// Catch all for remaining damage kinds (TODO: differentiate further with
/// staff/sceptre reworks
Energy,
}
const PIERCING_PENETRATION_FRACTION: f32 = 1.0;
const SLASHING_ENERGY_FRACTION: f32 = 1.0;
const CRUSHING_POISE_FRACTION: f32 = 1.0;
#[cfg(not(target_arch = "wasm32"))]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Damage {
@ -675,9 +754,9 @@ pub struct Damage {
impl Damage {
/// Returns the total damage reduction provided by all equipped items
pub fn compute_damage_reduction(
damage: Option<Self>,
inventory: Option<&Inventory>,
stats: Option<&Stats>,
kind: Option<DamageKind>,
) -> f32 {
let inventory_dr = if let Some(inventory) = inventory {
let protection = inventory
@ -696,12 +775,17 @@ impl Damage {
})
.sum::<Option<f32>>();
let kind_modifier = if matches!(kind, Some(DamageKind::Piercing)) {
0.75
let penetration = if let Some(damage) = damage {
if let DamageKind::Piercing = damage.kind {
damage.value * PIERCING_PENETRATION_FRACTION
} else {
0.0
}
} else {
1.0
0.0
};
let protection = protection.map(|dr| dr * kind_modifier);
let protection = protection.map(|p| p - penetration);
const FIFTY_PERCENT_DR_THRESHOLD: f32 = 60.0;
@ -971,7 +1055,7 @@ pub fn combat_rating(
// Normalized with a standard max health of 100
let health_rating = health.base_max()
/ 100.0
/ (1.0 - Damage::compute_damage_reduction(Some(inventory), None, None)).max(0.00001);
/ (1.0 - Damage::compute_damage_reduction(None, Some(inventory), None)).max(0.00001);
// Normalized with a standard max energy of 100 and energy reward multiplier of
// x1

View File

@ -96,7 +96,7 @@ pub use self::{
},
player::DisconnectReason,
player::{AliasError, Player, MAX_ALIAS_LEN},
poise::{Poise, PoiseState},
poise::{Poise, PoiseChange, PoiseState},
projectile::{Projectile, ProjectileConstructor},
shockwave::{Shockwave, ShockwaveHitEntities},
skillset::{

View File

@ -1,4 +1,5 @@
use crate::{
combat::{DamageContributor, DamageSource},
comp::{
self,
inventory::item::{armor::Protection, ItemKind},
@ -13,6 +14,20 @@ use specs_idvs::IdvStorage;
use std::{ops::Mul, time::Duration};
use vek::*;
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PoiseChange {
/// The amount of the poise change
pub amount: f32,
/// The direction that the poise change came from, used for when the target
/// is knocked down
pub impulse: Vec3<f32>,
/// The the individual or group who caused the poise change (None if the
/// damage wasn't caused by an entity)
pub by: Option<DamageContributor>,
/// The category of action that resulted in the poise change
pub cause: Option<DamageSource>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
/// Poise is represented by u32s within the module, but treated as a float by
/// the rest of the game.
@ -149,11 +164,11 @@ impl Poise {
}
}
pub fn change_by(&mut self, change: f32, impulse: Vec3<f32>) {
self.current = (((self.current() + change).clamp(0.0, f32::from(Self::MAX_POISE))
pub fn change(&mut self, change: PoiseChange) {
self.current = (((self.current() + change.amount).clamp(0.0, f32::from(Self::MAX_POISE))
* Self::SCALING_FACTOR_FLOAT) as u32)
.min(self.maximum);
self.last_change = Dir::from_unnormalized(impulse).unwrap_or_default();
self.last_change = Dir::from_unnormalized(change.impulse).unwrap_or_default();
}
pub fn reset(&mut self) { self.current = self.maximum; }

View File

@ -53,8 +53,7 @@ pub enum ServerEvent {
},
PoiseChange {
entity: EcsEntity,
change: f32,
kb_dir: Vec3<f32>,
change: comp::PoiseChange,
},
Delete(EcsEntity),
Destroy {

View File

@ -226,6 +226,7 @@ impl<'a> System<'a> for Sys {
pos: pos_b.0,
ori: read_data.orientations.get(target),
char_state: read_data.character_states.get(target),
energy: read_data.energies.get(target),
};
// PvP check

View File

@ -165,9 +165,9 @@ impl<'a> System<'a> for Sys {
}
let damage_reduction = Damage::compute_damage_reduction(
None,
read_data.inventories.get(entity),
Some(&stat),
None,
);
if (damage_reduction - 1.0).abs() < f32::EPSILON {
for (id, buff) in buff_comp.buffs.iter() {

View File

@ -164,6 +164,7 @@ impl<'a> System<'a> for Sys {
pos: pos_b.0,
ori: read_data.orientations.get(target),
char_state: read_data.char_states.get(target),
energy: read_data.energies.get(target),
};
// PvP check

View File

@ -279,6 +279,7 @@ fn dispatch_hit(
pos: target_pos,
ori: projectile_target_info.ori,
char_state: read_data.character_states.get(target),
energy: read_data.energies.get(target),
};
// TODO: Is it possible to have projectile without body??

View File

@ -207,6 +207,7 @@ impl<'a> System<'a> for Sys {
pos: pos_b.0,
ori: read_data.orientations.get(target),
char_state: read_data.character_states.get(target),
energy: read_data.energies.get(target),
};
// PvP check

View File

@ -3,8 +3,8 @@ use common::{
comp::{
self,
skills::{GeneralSkill, Skill},
Body, CharacterState, Combo, Energy, Health, Inventory, Poise, Pos, SkillSet, Stats,
StatsModifier,
Body, CharacterState, Combo, Energy, Health, Inventory, Poise, PoiseChange, Pos, SkillSet,
Stats, StatsModifier,
},
event::{EventBus, ServerEvent},
resources::{DeltaTime, EntitiesDiedLastTick, Time},
@ -173,7 +173,13 @@ impl<'a> System<'a> for Sys {
if res_poise {
let poise = &mut *poise;
poise.change_by(poise.regen_rate * dt, Vec3::zero());
let poise_change = PoiseChange {
amount: poise.regen_rate * dt,
impulse: Vec3::zero(),
by: None,
cause: None,
};
poise.change(poise_change);
poise.regen_rate = (poise.regen_rate + POISE_REGEN_ACCEL * dt).min(10.0);
}
},

View File

@ -48,14 +48,29 @@ enum DamageContrib {
NotFound,
}
pub fn handle_poise(server: &Server, entity: EcsEntity, change: f32, knockback_dir: Vec3<f32>) {
pub fn handle_poise(server: &Server, entity: EcsEntity, change: comp::PoiseChange) {
let ecs = &server.state.ecs();
if let Some(character_state) = ecs.read_storage::<CharacterState>().get(entity) {
// Entity is invincible to poise change during stunned/staggered character state
// Entity is invincible to poise change during stunned/staggered character
// state, but the mitigated poise damage is converted to health damage instead
if !character_state.is_stunned() {
if let Some(mut poise) = ecs.write_storage::<Poise>().get_mut(entity) {
poise.change_by(change, knockback_dir);
poise.change(change);
}
} else {
// TODO: Look into multiplying health by some fraction dependent on poise state
let time = ecs.read_resource::<Time>();
let health_change = HealthChange {
amount: change.amount,
by: None,
cause: None,
time: *time,
};
let server_eventbus = ecs.read_resource::<EventBus<ServerEvent>>();
server_eventbus.emit_now(ServerEvent::HealthChange {
entity,
change: health_change,
});
}
}
}
@ -521,9 +536,9 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
value: falldmg,
};
let damage_reduction = Damage::compute_damage_reduction(
Some(damage),
inventories.get(entity),
stats.get(entity),
Some(DamageKind::Crushing),
);
let time = server.state.ecs().read_resource::<Time>();
let change =
@ -534,7 +549,13 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
if let Some(mut poise) = ecs.write_storage::<comp::Poise>().get_mut(entity) {
let poise_damage = -(mass.0 * vel.magnitude_squared() / 1500.0);
let poise_change = Poise::apply_poise_reduction(poise_damage, inventories.get(entity));
poise.change_by(poise_change, Vec3::unit_z());
let poise_change = comp::PoiseChange {
amount: poise_change,
impulse: Vec3::unit_z(),
by: None,
cause: None,
};
poise.change(poise_change);
}
}
}
@ -857,6 +878,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
pos: pos_b.0,
ori: ori_b_maybe,
char_state: char_state_b_maybe,
energy: energies.get(entity_b),
};
// PvP check

View File

@ -96,11 +96,7 @@ impl Server {
ServerEvent::HealthChange { entity, change } => {
handle_health_change(self, entity, change)
},
ServerEvent::PoiseChange {
entity,
change,
kb_dir,
} => handle_poise(self, entity, change, kb_dir),
ServerEvent::PoiseChange { entity, change } => handle_poise(self, entity, change),
ServerEvent::Delete(entity) => handle_delete(self, entity),
ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause),
ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip),

View File

@ -139,9 +139,9 @@ impl StateExt for State {
let time = self.ecs().read_resource::<Time>();
let change = damage.calculate_health_change(
combat::Damage::compute_damage_reduction(
Some(damage),
inventories.get(entity),
stats.get(entity),
Some(damage.kind),
),
damage_contributor,
false,
@ -164,10 +164,22 @@ impl StateExt for State {
.get(entity)
{
if !character_state.is_stunned() {
let groups = self.ecs().read_storage::<comp::Group>();
let damage_contributor = source.and_then(|uid| {
self.ecs().entity_from_uid(uid.0).map(|attacker_entity| {
DamageContributor::new(uid, groups.get(attacker_entity).cloned())
})
});
let poise_change = comp::PoiseChange {
amount: change,
impulse: Vec3::zero(),
cause: None,
by: damage_contributor,
};
self.ecs()
.write_storage::<comp::Poise>()
.get_mut(entity)
.map(|mut poise| poise.change_by(change, Vec3::zero()));
.map(|mut poise| poise.change(poise_change));
}
}
},

View File

@ -935,7 +935,7 @@ impl<'a> Widget for Bag<'a> {
let protection_txt = format!(
"{}%",
(100.0
* Damage::compute_damage_reduction(Some(inventory), Some(self.stats), None,))
* Damage::compute_damage_reduction(None, Some(inventory), Some(self.stats)))
as i32
);
let health_txt = format!("{}", self.health.maximum().round() as usize);