mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
parent
c6fc42be82
commit
d1e1de3b14
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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::{
|
||||
|
@ -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; }
|
||||
|
@ -53,8 +53,7 @@ pub enum ServerEvent {
|
||||
},
|
||||
PoiseChange {
|
||||
entity: EcsEntity,
|
||||
change: f32,
|
||||
kb_dir: Vec3<f32>,
|
||||
change: comp::PoiseChange,
|
||||
},
|
||||
Delete(EcsEntity),
|
||||
Destroy {
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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??
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user