From f17c5661ea31cfe5d88041aa8e1370a9d749cb9d Mon Sep 17 00:00:00 2001 From: Papinha Date: Mon, 27 May 2024 22:01:28 +0000 Subject: [PATCH] Parried debuff rework --- common/src/combat.rs | 17 ++++++++- common/src/comp/ability.rs | 1 + common/src/comp/buff.rs | 9 ++++- common/src/comp/stats.rs | 4 +++ common/src/event.rs | 1 + common/src/states/basic_aura.rs | 6 +++- common/src/states/basic_beam.rs | 6 +++- common/src/states/basic_block.rs | 12 +++++-- common/src/states/basic_melee.rs | 6 +++- common/src/states/basic_ranged.rs | 6 +++- common/src/states/basic_summon.rs | 6 +++- common/src/states/blink.rs | 6 +++- common/src/states/charged_melee.rs | 6 +++- common/src/states/charged_ranged.rs | 6 +++- common/src/states/combo_melee2.rs | 6 +++- common/src/states/dash_melee.rs | 6 +++- common/src/states/dive_melee.rs | 6 +++- common/src/states/finisher_melee.rs | 6 +++- common/src/states/leap_melee.rs | 12 +++++-- common/src/states/leap_shockwave.rs | 6 +++- common/src/states/rapid_melee.rs | 6 +++- common/src/states/repeater_ranged.rs | 6 +++- common/src/states/riposte_melee.rs | 6 +++- common/src/states/roll.rs | 6 +++- common/src/states/self_buff.rs | 6 +++- common/src/states/shockwave.rs | 6 +++- common/src/states/sprite_interact.rs | 6 +++- common/src/states/sprite_summon.rs | 6 +++- common/src/states/static_aura.rs | 6 +++- common/src/states/stunned.rs | 6 +++- common/src/states/transform.rs | 6 +++- common/src/states/use_item.rs | 6 +++- common/systems/src/buff.rs | 10 ++++++ server/src/events/entity_manipulation.rs | 45 +++++++++++++++++++++--- 34 files changed, 225 insertions(+), 36 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 759db36914..4cf12646af 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -63,6 +63,7 @@ pub const MAX_BLOCK_POISE_COST: f32 = 25.0; pub const PARRY_BONUS_MULTIPLIER: f32 = 2.0; pub const FALLBACK_BLOCK_STRENGTH: f32 = 5.0; pub const BEHIND_TARGET_ANGLE: f32 = 45.0; +pub const BASE_PARRIED_POISE_PUNISHMENT: f32 = 100.0 / 3.5; #[derive(Copy, Clone)] pub struct AttackerInfo<'a> { @@ -76,6 +77,7 @@ pub struct AttackerInfo<'a> { pub mass: Option<&'a Mass>, } +#[derive(Copy, Clone)] pub struct TargetInfo<'a> { pub entity: EcsEntity, pub uid: Uid, @@ -187,6 +189,7 @@ impl Attack { defender: target.entity, attacker: attacker.map(|a| a.entity), source, + poise_multiplier: 2.0 - (damage_value / block_strength).min(1.0), }); } @@ -301,10 +304,22 @@ impl Attack { (matches!(attack_effect.target, Some(GroupTarget::OutOfGroup)) && !allow_friendly_fire) && (target_dodging || !permit_pvp) }; - let precision_mult = attacker + + let from_precision_mult = attacker .and_then(|a| a.stats) .and_then(|s| s.precision_multiplier_override) .or(precision_mult); + + let from_precision_vulnerability_mult = target + .stats + .and_then(|s| s.precision_vulnerability_multiplier_override); + + let precision_mult = match (from_precision_mult, from_precision_vulnerability_mult) { + (Some(a), Some(b)) => Some(a.max(b)), + (Some(a), None) | (None, Some(a)) => Some(a), + (None, None) => None, + }; + let mut is_applied = false; let mut accumulated_damage = 0.0; let damage_modifier = attacker diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 6d9cc5dcbb..87a5680be3 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -2402,6 +2402,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { }, timer: Duration::default(), stage_section: StageSection::Buildup, + is_parry: false, }), CharacterAbility::Roll { energy_cost: _, diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index 51a4c1d877..5b588f7004 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -436,7 +436,10 @@ impl BuffKind { BuffEffect::PoiseReduction(nn_scaling(data.strength)), BuffEffect::PoiseDamageFromLostHealth(data.strength), ], - BuffKind::Parried => vec![BuffEffect::AttackSpeed(0.5)], + BuffKind::Parried => vec![ + BuffEffect::RecoverySpeed(0.25), + BuffEffect::PrecisionVulnerabilityOverride(1.0), + ], //TODO: Handle potion sickness in a more general way. BuffKind::PotionSickness => vec![ BuffEffect::HealReduction(data.strength), @@ -672,6 +675,8 @@ pub enum BuffEffect { MovementSpeed(f32), /// Modifies attack speed of target AttackSpeed(f32), + /// Modifies recovery speed of target + RecoverySpeed(f32), /// Modifies ground friction of target GroundFriction(f32), /// Reduces poise damage taken after armor is accounted for by this fraction @@ -686,6 +691,8 @@ pub enum BuffEffect { AttackDamage(f32), /// Overrides the precision multiplier applied to an attack PrecisionOverride(f32), + /// Overrides the precision multiplier applied to an incoming attack + PrecisionVulnerabilityOverride(f32), /// Changes body. BodyChange(Body), BuffImmunity(BuffKind), diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index 0ba3c3ad36..a30141c4a8 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -61,11 +61,13 @@ pub struct Stats { pub move_speed_modifier: f32, pub jump_modifier: f32, pub attack_speed_modifier: f32, + pub recovery_speed_modifier: f32, pub friction_modifier: f32, pub max_energy_modifiers: StatsModifier, pub poise_damage_modifier: f32, pub attack_damage_modifier: f32, pub precision_multiplier_override: Option, + pub precision_vulnerability_multiplier_override: Option, pub swim_speed_modifier: f32, /// This adds effects to any attacks that the entity makes pub effects_on_attack: Vec, @@ -93,11 +95,13 @@ impl Stats { move_speed_modifier: 1.0, jump_modifier: 1.0, attack_speed_modifier: 1.0, + recovery_speed_modifier: 1.0, friction_modifier: 1.0, max_energy_modifiers: StatsModifier::default(), poise_damage_modifier: 1.0, attack_damage_modifier: 1.0, precision_multiplier_override: None, + precision_vulnerability_multiplier_override: None, swim_speed_modifier: 1.0, effects_on_attack: Vec::new(), mitigations_penetration: 0.0, diff --git a/common/src/event.rs b/common/src/event.rs index 67d5f22d67..684d1c3451 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -332,6 +332,7 @@ pub struct ParryHookEvent { pub defender: EcsEntity, pub attacker: Option, pub source: AttackSource, + pub poise_multiplier: f32, } pub struct RequestSiteInfoEvent { diff --git a/common/src/states/basic_aura.rs b/common/src/states/basic_aura.rs index 077010c31d..ae6527bbde 100644 --- a/common/src/states/basic_aura.rs +++ b/common/src/states/basic_aura.rs @@ -134,7 +134,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { update.character = CharacterState::BasicAura(Data { static_data: self.static_data.clone(), - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index eb7c936cd1..76d82553db 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -222,7 +222,11 @@ impl CharacterBehavior for Data { StageSection::Recover => { if self.timer < self.static_data.recover_duration { update.character = CharacterState::BasicBeam(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index b21984f42b..33c001898a 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -51,6 +51,8 @@ pub struct Data { pub timer: Duration, /// What section the character stage is in pub stage_section: StageSection, + // Whether there was parry + pub is_parry: bool, } impl CharacterBehavior for Data { @@ -100,10 +102,16 @@ impl CharacterBehavior for Data { } }, StageSection::Recover => { - if self.timer < self.static_data.recover_duration { + if (self.static_data.parry_window.recover || !self.is_parry) + && self.timer < self.static_data.recover_duration + { // Recovery update.character = CharacterState::BasicBlock(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 156e3ac1f4..13743ac0cc 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -133,7 +133,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovery update.character = CharacterState::BasicMelee(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 879e06fad6..b04ddcd026 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -154,7 +154,11 @@ impl CharacterBehavior for Data { } else if self.timer < self.static_data.recover_duration { // Recovers update.character = CharacterState::BasicRanged(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 17fa0ffb7f..c648a1df9d 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -249,7 +249,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovery update.character = CharacterState::BasicSummon(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/blink.rs b/common/src/states/blink.rs index 8eabcef139..7ab1ad1df5 100644 --- a/common/src/states/blink.rs +++ b/common/src/states/blink.rs @@ -78,7 +78,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovery update.character = CharacterState::Blink(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 9fac060941..a1a8aff235 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -206,7 +206,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovers update.character = CharacterState::ChargedMelee(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 7f72ebb29b..692f8d5276 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -171,7 +171,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovers update.character = CharacterState::ChargedRanged(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/combo_melee2.rs b/common/src/states/combo_melee2.rs index 530e4dcce1..2ecf0a8936 100644 --- a/common/src/states/combo_melee2.rs +++ b/common/src/states/combo_melee2.rs @@ -234,7 +234,11 @@ impl CharacterBehavior for Data { if self.timer < strike_data.recover_duration { // Recovery if let CharacterState::ComboMelee2(c) = &mut update.character { - c.timer = tick_attack_or_default(data, self.timer, None); + c.timer = tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ); } } else { // Return to wielding diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 8118cff0cc..89f7b594a7 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -170,7 +170,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recover update.character = CharacterState::DashMelee(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/dive_melee.rs b/common/src/states/dive_melee.rs index 9a11cd2757..014ebe29f6 100644 --- a/common/src/states/dive_melee.rs +++ b/common/src/states/dive_melee.rs @@ -131,7 +131,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Complete recovery delay before finishing state if let CharacterState::DiveMelee(c) = &mut update.character { - c.timer = tick_attack_or_default(data, self.timer, None); + c.timer = tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ); } } else { // Done diff --git a/common/src/states/finisher_melee.rs b/common/src/states/finisher_melee.rs index b38faf6f11..bc2f2651b3 100644 --- a/common/src/states/finisher_melee.rs +++ b/common/src/states/finisher_melee.rs @@ -128,7 +128,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovery if let CharacterState::FinisherMelee(c) = &mut update.character { - c.timer = tick_attack_or_default(data, self.timer, None); + c.timer = tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ); } } else { // Done diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 0e89f3f0e8..882138d1e4 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -140,14 +140,22 @@ impl CharacterBehavior for Data { ); update.character = CharacterState::LeapMelee(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), exhausted: true, ..*self }); } else if self.timer < self.static_data.recover_duration { // Complete recovery delay before finishing state update.character = CharacterState::LeapMelee(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/leap_shockwave.rs b/common/src/states/leap_shockwave.rs index cbf39a7da5..15c185296f 100644 --- a/common/src/states/leap_shockwave.rs +++ b/common/src/states/leap_shockwave.rs @@ -248,7 +248,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovers update.character = CharacterState::LeapShockwave(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/rapid_melee.rs b/common/src/states/rapid_melee.rs index 37c28e51f8..ae594f7baf 100644 --- a/common/src/states/rapid_melee.rs +++ b/common/src/states/rapid_melee.rs @@ -127,7 +127,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recover if let CharacterState::RapidMelee(c) = &mut update.character { - c.timer = tick_attack_or_default(data, self.timer, None); + c.timer = tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ); } } else { // Done diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index cb7fc9ee81..253f0e4498 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -187,7 +187,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recover from attack update.character = CharacterState::RepeaterRanged(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/riposte_melee.rs b/common/src/states/riposte_melee.rs index c9bb66ea60..af145a2b24 100644 --- a/common/src/states/riposte_melee.rs +++ b/common/src/states/riposte_melee.rs @@ -94,7 +94,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovery if let CharacterState::RiposteMelee(c) = &mut update.character { - c.timer = tick_attack_or_default(data, self.timer, None); + c.timer = tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ); } } else { // Done diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index 0b038e819b..bf433d4386 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -119,7 +119,11 @@ impl CharacterBehavior for Data { { // Recover update.character = CharacterState::Roll(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/self_buff.rs b/common/src/states/self_buff.rs index ac070567d0..ee65870985 100644 --- a/common/src/states/self_buff.rs +++ b/common/src/states/self_buff.rs @@ -170,7 +170,11 @@ impl CharacterBehavior for Data { StageSection::Recover => { if self.timer < self.static_data.recover_duration { update.character = CharacterState::SelfBuff(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index 06c34939f5..79692773ff 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -174,7 +174,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovers update.character = CharacterState::Shockwave(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs index 63269afb44..12c5b6b2f1 100644 --- a/common/src/states/sprite_interact.rs +++ b/common/src/states/sprite_interact.rs @@ -95,7 +95,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovery if let CharacterState::SpriteInteract(c) = &mut update.character { - c.timer = tick_attack_or_default(data, self.timer, None); + c.timer = tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ); } } else { // Create inventory manipulation event diff --git a/common/src/states/sprite_summon.rs b/common/src/states/sprite_summon.rs index 1ef7ff7911..efd039e876 100644 --- a/common/src/states/sprite_summon.rs +++ b/common/src/states/sprite_summon.rs @@ -183,7 +183,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovery update.character = CharacterState::SpriteSummon(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/static_aura.rs b/common/src/states/static_aura.rs index 37d1b31d6b..7f166464c3 100644 --- a/common/src/states/static_aura.rs +++ b/common/src/states/static_aura.rs @@ -140,7 +140,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { update.character = CharacterState::StaticAura(Data { static_data: self.static_data.clone(), - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/stunned.rs b/common/src/states/stunned.rs index 1524564d37..7c4576b7b6 100644 --- a/common/src/states/stunned.rs +++ b/common/src/states/stunned.rs @@ -66,7 +66,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovery update.character = CharacterState::Stunned(Data { - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/transform.rs b/common/src/states/transform.rs index 83c67d07ef..4461c7bb17 100644 --- a/common/src/states/transform.rs +++ b/common/src/states/transform.rs @@ -114,7 +114,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { update.character = CharacterState::Transform(Data { static_data: self.static_data.clone(), - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/src/states/use_item.rs b/common/src/states/use_item.rs index b7fcf9b23a..ac32afde4f 100644 --- a/common/src/states/use_item.rs +++ b/common/src/states/use_item.rs @@ -120,7 +120,11 @@ impl CharacterBehavior for Data { // Recovery update.character = CharacterState::UseItem(Data { static_data: self.static_data.clone(), - timer: tick_attack_or_default(data, self.timer, None), + timer: tick_attack_or_default( + data, + self.timer, + Some(data.stats.recovery_speed_modifier), + ), ..*self }); } else { diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index 6347b102fc..429975738b 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -755,6 +755,9 @@ fn execute_effect( BuffEffect::AttackSpeed(speed) => { stat.attack_speed_modifier *= *speed; }, + BuffEffect::RecoverySpeed(speed) => { + stat.recovery_speed_modifier *= *speed; + }, BuffEffect::GroundFriction(gf) => { stat.friction_modifier *= *gf; }, @@ -781,6 +784,13 @@ fn execute_effect( .map(|mult| mult.min(*val)) .or(Some(*val)); }, + BuffEffect::PrecisionVulnerabilityOverride(val) => { + // Use higher of precision multiplier overrides + stat.precision_vulnerability_multiplier_override = stat + .precision_vulnerability_multiplier_override + .map(|mult| mult.max(*val)) + .or(Some(*val)); + }, BuffEffect::BodyChange(b) => { // For when an entity is under the effects of multiple de/buffs that change the // body, to avoid flickering between many bodies only change the body if the diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index ad66f2fa23..1d5a70c732 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -18,7 +18,7 @@ use crate::{ #[cfg(feature = "worldgen")] use common::rtsim::{Actor, RtSimEntity}; use common::{ - combat::{self, AttackSource, DamageContributor, DeathEffect}, + combat::{self, AttackSource, DamageContributor, DeathEffect, BASE_PARRIED_POISE_PUNISHMENT}, comp::{ self, aura::{self, EnteredAuras}, @@ -28,7 +28,7 @@ use common::{ item::flatten_counted_items, loot_owner::LootOwnerKind, Alignment, Auras, Body, CharacterState, Energy, Group, Health, Inventory, Object, - PickupItem, Player, Poise, Pos, Presence, PresenceKind, SkillSet, Stats, + PickupItem, Player, Poise, PoiseChange, Pos, Presence, PresenceKind, SkillSet, Stats, BASE_ABILITY_LIMIT, }, consts::TELEPORTER_RADIUS, @@ -1746,18 +1746,31 @@ impl ServerEvent for ParryHookEvent { type SystemData<'a> = ( Read<'a, Time>, Read<'a, EventBus>, + Read<'a, EventBus>, Read<'a, EventBus>, WriteStorage<'a, CharacterState>, ReadStorage<'a, Uid>, ReadStorage<'a, Stats>, ReadStorage<'a, comp::Mass>, + ReadStorage<'a, Inventory>, ); fn handle( events: impl ExactSizeIterator, - (time, energy_change_events, buff_events, mut character_states, uids, stats, masses): Self::SystemData<'_>, + ( + time, + energy_change_events, + poise_change_events, + buff_events, + mut character_states, + uids, + stats, + masses, + inventories, + ): Self::SystemData<'_>, ) { let mut energy_change_emitter = energy_change_events.emitter(); + let mut poise_change_emitter = poise_change_events.emitter(); let mut buff_emitter = buff_events.emitter(); for ev in events { if let Some(mut char_state) = character_states.get_mut(ev.defender) { @@ -1773,6 +1786,7 @@ impl ServerEvent for ParryHookEvent { entity: ev.defender, change: c.static_data.energy_regen, }); + c.is_parry = true; false }, _ => false, @@ -1787,8 +1801,8 @@ impl ServerEvent for ParryHookEvent { if let Some(attacker) = ev.attacker && matches!(ev.source, AttackSource::Melee) { - // When attacker is parried, add the parried debuff for 2 seconds, which slows - // them + // When attacker is parried, the debuff lasts 2 seconds, the attacker takes + // poise damage, get precision vulnerability and get slower recovery speed let data = buff::BuffData::new(1.0, Some(Secs(2.0))); let source = if let Some(uid) = uids.get(ev.defender) { BuffSource::Character { by: *uid } @@ -1812,6 +1826,27 @@ impl ServerEvent for ParryHookEvent { entity: attacker, buff_change: buff::BuffChange::Add(buff), }); + + let attacker_poise_change = Poise::apply_poise_reduction( + ev.poise_multiplier.clamp(1.0, 2.0) * BASE_PARRIED_POISE_PUNISHMENT, + inventories.get(attacker), + &MaterialStatManifest::load().read(), + character_states.get(attacker), + stats.get(attacker), + ); + + poise_change_emitter.emit(PoiseChangeEvent { + entity: attacker, + change: PoiseChange { + amount: -attacker_poise_change, + impulse: Vec3::zero(), + by: uids + .get(ev.defender) + .map(|d| DamageContributor::new(*d, None)), + cause: Some(DamageSource::Melee), + time: *time, + }, + }); } } }