diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index dd56229a4f..202916a41b 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -233,7 +233,7 @@ ], ), Simple(Hammer(PileDriver), "common.abilities.hammer.pile_driver"), - // Simple(Hammer(LungPummel), "common.abilities.hammer.lung_pummel"), + Simple(Hammer(LungPummel), "common.abilities.hammer.lung_pummel"), // Simple(Hammer(HelmCrusher), "common.abilities.hammer.helm_crusher"), // Simple(Hammer(Rampart), "common.abilities.hammer.rampart"), // Simple(Hammer(Tenacity), "common.abilities.hammer.tenacity"), diff --git a/assets/common/abilities/hammer/breach.ron b/assets/common/abilities/hammer/breach.ron index 38fba8b8be..4350098b5c 100644 --- a/assets/common/abilities/hammer/breach.ron +++ b/assets/common/abilities/hammer/breach.ron @@ -14,7 +14,7 @@ BasicMelee( range: 3.0, angle: 15.0, attack_effect: Some((Poise(40), TargetBlocking)), - precision_flank_multiplier: 1.5, + precision_flank_multipliers: (back: 1.0, side: 1.0, front: 1.5), precision_flank_invert: true, ), ori_modifier: 0.6, diff --git a/assets/common/abilities/hammer/lung_pummel.ron b/assets/common/abilities/hammer/lung_pummel.ron new file mode 100644 index 0000000000..64deb87eb5 --- /dev/null +++ b/assets/common/abilities/hammer/lung_pummel.ron @@ -0,0 +1,25 @@ +FinisherMelee( + energy_cost: 0, + buildup_duration: 0.15, + swing_duration: 0.15, + recover_duration: 0.2, + melee_constructor: ( + kind: Bash( + damage: 20, + poise: 20, + knockback: 12, + energy_regen: 0, + ), + range: 4.0, + angle: 60.0, + damage_effect: Some(Buff(( + kind: Winded, + dur_secs: 8, + strength: Value(1.0), + chance: 1.0, + ))), + precision_flank_multipliers: (front: 1.0, side: 2.0, back: 1.0), + ), + minimum_combo: 0, + combo_consumption: Cost, +) diff --git a/assets/common/abilities/hammer/spine_cracker.ron b/assets/common/abilities/hammer/spine_cracker.ron index 23ec67d73a..329ee0e08e 100644 --- a/assets/common/abilities/hammer/spine_cracker.ron +++ b/assets/common/abilities/hammer/spine_cracker.ron @@ -13,7 +13,7 @@ FinisherMelee( range: 4.0, angle: 15.0, attack_effect: Some((Poise(50), BehindTarget)), - precision_flank_multiplier: 2.0, + precision_flank_multipliers: (front: 1.0, side: 1.0, back: 2.0), ), minimum_combo: 10, combo_consumption: Cost, diff --git a/assets/voxygen/element/de_buffs/debuff_winded.png b/assets/voxygen/element/de_buffs/debuff_winded.png new file mode 100644 index 0000000000..763accec2a --- /dev/null +++ b/assets/voxygen/element/de_buffs/debuff_winded.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e38da416a0e92cc4301135b3c74f61aa6390e225a7419d155d70eeddff5b5f0c +size 1011 diff --git a/assets/voxygen/element/skills/hammer/lung_pummel.png b/assets/voxygen/element/skills/hammer/lung_pummel.png new file mode 100644 index 0000000000..920d68aa5c --- /dev/null +++ b/assets/voxygen/element/skills/hammer/lung_pummel.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:731e02691015ec5a99186c8ad781d6996a60f8515c663e9a279f91c87df6c47c +size 1018 diff --git a/assets/voxygen/i18n/en/buff.ftl b/assets/voxygen/i18n/en/buff.ftl index 815e05913c..f59ef7ae7b 100644 --- a/assets/voxygen/i18n/en/buff.ftl +++ b/assets/voxygen/i18n/en/buff.ftl @@ -127,6 +127,9 @@ buff-scornfultaunt = Scornful Taunt ## Rooted buff-rooted = Rooted .desc = You are stuck in place and cannot move. +## Winded +buff-winded = Winded + .desc = You can barely breathe hampering how much energy you can recover and how quickly you can move. ## Util buff-text-over_seconds = over { $dur_secs } seconds buff-text-for_seconds = for { $dur_secs } seconds diff --git a/assets/voxygen/i18n/en/hud/ability.ftl b/assets/voxygen/i18n/en/hud/ability.ftl index e94a379286..f4158fc9e9 100644 --- a/assets/voxygen/i18n/en/hud/ability.ftl +++ b/assets/voxygen/i18n/en/hud/ability.ftl @@ -416,3 +416,6 @@ common-abilities-hammer-breach = Breach common-abilities-hammer-pile_driver = Pile Driver .desc = Strike your enemy into the ground, stopping their movement until they free their legs. +common-abilities-hammer-lung_pummel = Lung Pummel + .desc = + Swipe your hammer into your foe's side, winding them. diff --git a/common/src/cmd.rs b/common/src/cmd.rs index f3fc520ece..23d13322e4 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -191,6 +191,7 @@ lazy_static! { BuffKind::Heatstroke => "heatstroke", BuffKind::ScornfulTaunt => "scornful_taunt", BuffKind::Rooted => "rooted", + BuffKind::Winded => "winded", }; let mut buff_parser = HashMap::new(); for kind in BuffKind::iter() { diff --git a/common/src/combat.rs b/common/src/combat.rs index 079bf82dd5..df49bd25c0 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -2,7 +2,7 @@ use crate::{ comp::{ ability::Capability, aura::{AuraKindVariant, EnteredAuras}, - buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource}, + buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource, DestInfo}, inventory::{ item::{ armor::Protection, @@ -13,7 +13,7 @@ use crate::{ }, skillset::SkillGroupKind, Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, HealthChange, - Inventory, Ori, Player, Poise, PoiseChange, SkillSet, Stats, + Inventory, Mass, Ori, Player, Poise, PoiseChange, SkillSet, Stats, }, event::{ BuffEvent, ComboChangeEvent, EmitExt, EnergyChangeEvent, EntityAttackedHookEvent, @@ -72,6 +72,7 @@ pub struct AttackerInfo<'a> { pub combo: Option<&'a Combo>, pub inventory: Option<&'a Inventory>, pub stats: Option<&'a Stats>, + pub mass: Option<&'a Mass>, } pub struct TargetInfo<'a> { @@ -85,6 +86,7 @@ pub struct TargetInfo<'a> { pub char_state: Option<&'a CharacterState>, pub energy: Option<&'a Energy>, pub buffs: Option<&'a Buffs>, + pub mass: Option<&'a Mass>, } #[derive(Clone, Copy)] @@ -464,8 +466,8 @@ impl Attack { entity: target.entity, buff_change: BuffChange::Add(b.to_buff( time, - attacker.map(|a| a.uid), - target.stats, + attacker, + target, applied_damage, strength_modifier, )), @@ -697,8 +699,8 @@ impl Attack { entity: target.entity, buff_change: BuffChange::Add(b.to_buff( time, - attacker.map(|a| a.uid), - target.stats, + attacker, + target, accumulated_damage, strength_modifier, )), @@ -1322,17 +1324,21 @@ impl CombatBuff { fn to_buff( self, time: Time, - uid: Option, - tgt_stats: Option<&Stats>, + attacker_info: Option, + target_info: &TargetInfo, damage: f32, strength_modifier: f32, ) -> Buff { // TODO: Generate BufCategoryId vec (probably requires damage overhaul?) - let source = if let Some(uid) = uid { + let source = if let Some(uid) = attacker_info.map(|a| a.uid) { BuffSource::Character { by: uid } } else { BuffSource::Unknown }; + let dest_info = DestInfo { + stats: target_info.stats, + mass: target_info.mass, + }; Buff::new( self.kind, BuffData::new( @@ -1342,7 +1348,8 @@ impl CombatBuff { Vec::new(), source, time, - tgt_stats, + dest_info, + attacker_info.and_then(|a| a.mass), ) } } @@ -1646,7 +1653,7 @@ pub fn compute_poise_resilience( pub fn precision_mult_from_flank( attack_dir: Vec3, target_ori: Option<&Ori>, - precision_flank_multiplier: f32, + precision_flank_multipliers: FlankMults, precision_flank_invert: bool, ) -> Option { let angle = target_ori.map(|t_ori| { @@ -1657,16 +1664,38 @@ pub fn precision_mult_from_flank( }) }); match angle { - Some(angle) if angle < FULL_FLANK_ANGLE => { - Some(MAX_BACK_FLANK_PRECISION * precision_flank_multiplier) - }, + Some(angle) if angle < FULL_FLANK_ANGLE => Some( + MAX_BACK_FLANK_PRECISION + * if precision_flank_invert { + precision_flank_multipliers.front + } else { + precision_flank_multipliers.back + }, + ), Some(angle) if angle < PARTIAL_FLANK_ANGLE => { - Some(MAX_SIDE_FLANK_PRECISION * precision_flank_multiplier) + Some(MAX_SIDE_FLANK_PRECISION * precision_flank_multipliers.side) }, Some(_) | None => None, } } +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct FlankMults { + pub back: f32, + pub front: f32, + pub side: f32, +} + +impl Default for FlankMults { + fn default() -> Self { + FlankMults { + back: 1.0, + front: 1.0, + side: 1.0, + } + } +} + pub fn block_strength(inventory: &Inventory, char_state: &CharacterState) -> f32 { match char_state { CharacterState::BasicBlock(data) => data.static_data.block_strength, diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index d1c1f5e194..5d951bfaa0 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1113,7 +1113,7 @@ impl Default for CharacterAbility { attack_effect: None, simultaneous_hits: 1, custom_combo: None, - precision_flank_multiplier: 1.0, + precision_flank_multipliers: Default::default(), precision_flank_invert: false, }, ori_modifier: 1.0, diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index e42fd79a2c..b99578d0a6 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -4,7 +4,7 @@ use crate::{ AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect, CombatRequirement, DamagedEffect, DeathEffect, }, - comp::{aura::AuraKey, Stats}, + comp::{aura::AuraKey, Mass, Stats}, resources::{Secs, Time}, uid::Uid, }; @@ -185,6 +185,11 @@ pub enum BuffKind { /// is higher than the strength allows for, duration gets reduced using a /// mutiplier from the ratio of masses. Rooted, + /// Slows movement speed and reduces energy reward + /// Both scale non-linearly with strength, 0.5 leads to 50% reduction of + /// energy reward and 33% reduction of move speed. 1.0 leads to 67% + /// reduction of energy reward and 50% reduction of move speed. + Winded, // Complex, non-obvious buffs /// Changed into another body. Polymorphed, @@ -248,7 +253,8 @@ impl BuffKind { | BuffKind::Parried | BuffKind::PotionSickness | BuffKind::Heatstroke - | BuffKind::Rooted => BuffDescriptor::SimpleNegative, + | BuffKind::Rooted + | BuffKind::Winded => BuffDescriptor::SimpleNegative, BuffKind::Polymorphed => BuffDescriptor::Complex, } } @@ -286,7 +292,10 @@ impl BuffKind { pub fn effects(&self, data: &BuffData, stats: Option<&Stats>) -> Vec { // Normalized nonlinear scaling + // TODO: Do we want to make denominator term parameterized. Come back to if we + // add nn_scaling3. let nn_scaling = |a| a / (a + 0.5); + let nn_scaling2 = |a| a / (a + 1.0); let instance = rand::random(); match self { BuffKind::Bleeding => vec![BuffEffect::HealthChangeOverTime { @@ -489,6 +498,10 @@ impl BuffKind { }), ], BuffKind::Rooted => vec![BuffEffect::MovementSpeed(0.0)], + BuffKind::Winded => vec![ + BuffEffect::MovementSpeed(1.0 - nn_scaling2(data.strength)), + BuffEffect::EnergyReward(1.0 - nn_scaling(data.strength)), + ], } } @@ -504,13 +517,18 @@ impl BuffKind { cat_ids } - fn modify_data(&self, mut data: BuffData) -> BuffData { + fn modify_data( + &self, + mut data: BuffData, + source_mass: Option<&Mass>, + dest_mass: Option<&Mass>, + ) -> BuffData { // TODO: Remove clippy allow after another buff needs this #[allow(clippy::single_match)] match self { BuffKind::Rooted => { - let source_mass = 50.0; - let dest_mass = 50.0_f64; + let source_mass = source_mass.map_or(50.0, |m| m.0 as f64); + let dest_mass = dest_mass.map_or(50.0, |m| m.0 as f64); let ratio = (source_mass / dest_mass).min(1.0); data.duration = data.duration.map(|dur| Secs(dur.0 * ratio)); }, @@ -710,10 +728,12 @@ impl Buff { cat_ids: Vec, source: BuffSource, time: Time, - stats: Option<&Stats>, + dest_info: DestInfo, + // Create source_info if we need more parameters from source + source_mass: Option<&Mass>, ) -> Self { - let data = kind.modify_data(data); - let effects = kind.effects(&data, stats); + let data = kind.modify_data(data, source_mass, dest_info.mass); + let effects = kind.effects(&data, dest_info.stats); let cat_ids = kind.extend_cat_ids(cat_ids); let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0)); let end_time = if cat_ids @@ -958,6 +978,12 @@ impl Component for Buffs { type Storage = DerefFlaggedStorage>; } +#[derive(Default, Copy, Clone)] +pub struct DestInfo<'a> { + pub stats: Option<&'a Stats>, + pub mass: Option<&'a Mass>, +} + #[cfg(test)] pub mod tests { use crate::comp::buff::*; @@ -973,6 +999,7 @@ pub mod tests { Vec::new(), BuffSource::Unknown, time, + DestInfo::default(), None, ) } diff --git a/common/src/comp/melee.rs b/common/src/comp/melee.rs index 7efa21c480..695b6d5e34 100644 --- a/common/src/comp/melee.rs +++ b/common/src/comp/melee.rs @@ -1,7 +1,8 @@ use crate::{ combat::{ Attack, AttackDamage, AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect, - CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir, + CombatRequirement, Damage, DamageKind, DamageSource, FlankMults, GroupTarget, Knockback, + KnockbackDir, }, comp::{ buff::BuffKind, @@ -23,7 +24,7 @@ pub struct Melee { pub multi_target: Option, pub break_block: Option<(Vec3, Option)>, pub simultaneous_hits: u32, - pub precision_flank_multiplier: f32, + pub precision_flank_multipliers: FlankMults, pub precision_flank_invert: bool, } @@ -53,7 +54,6 @@ impl Component for Melee { } fn default_simultaneous_hits() -> u32 { 1 } -fn default_precision_flank_multiplier() -> f32 { 1.0 } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Scaled { @@ -80,8 +80,8 @@ pub struct MeleeConstructor { #[serde(default = "default_simultaneous_hits")] pub simultaneous_hits: u32, pub custom_combo: Option, - #[serde(default = "default_precision_flank_multiplier")] - pub precision_flank_multiplier: f32, + #[serde(default)] + pub precision_flank_multipliers: FlankMults, #[serde(default)] pub precision_flank_invert: bool, } @@ -393,7 +393,7 @@ impl MeleeConstructor { multi_target: self.multi_target, break_block: None, simultaneous_hits: self.simultaneous_hits, - precision_flank_multiplier: self.precision_flank_multiplier, + precision_flank_multipliers: self.precision_flank_multipliers, precision_flank_invert: self.precision_flank_invert, } } diff --git a/common/src/states/self_buff.rs b/common/src/states/self_buff.rs index 85bc52372f..ac070567d0 100644 --- a/common/src/states/self_buff.rs +++ b/common/src/states/self_buff.rs @@ -1,6 +1,6 @@ use crate::{ comp::{ - buff::{Buff, BuffCategory, BuffChange, BuffData, BuffKind, BuffSource}, + buff::{Buff, BuffCategory, BuffChange, BuffData, BuffKind, BuffSource, DestInfo}, character_state::OutputEvents, CharacterState, StateUpdate, }, @@ -116,6 +116,11 @@ impl CharacterBehavior for Data { }); } + let dest_info = DestInfo { + stats: Some(data.stats), + mass: Some(data.mass), + }; + // Creates buff let buff = Buff::new( self.static_data.buff_kind, @@ -126,7 +131,8 @@ impl CharacterBehavior for Data { buff_cat_ids, BuffSource::Character { by: *data.uid }, *data.time, - Some(data.stats), + dest_info, + Some(data.mass), ); output_events.emit_server(BuffEvent { entity: data.entity, diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 9c0a53ca92..ec6d816564 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -3,7 +3,7 @@ use crate::{ comp::{ ability::{AbilityInitEvent, AbilityMeta, Capability, SpecifiedAbility, Stance}, arthropod, biped_large, biped_small, bird_medium, - buff::{Buff, BuffCategory, BuffChange, BuffData, BuffSource}, + buff::{Buff, BuffCategory, BuffChange, BuffData, BuffSource, DestInfo}, character_state::OutputEvents, controller::InventoryManip, golem, @@ -1332,6 +1332,10 @@ fn handle_ability( strength, duration, } => { + let dest_info = DestInfo { + stats: Some(data.stats), + mass: Some(data.mass), + }; output_events.emit_server(BuffEvent { entity: data.entity, buff_change: BuffChange::Add(Buff::new( @@ -1340,7 +1344,8 @@ fn handle_ability( vec![BuffCategory::SelfBuff], BuffSource::Character { by: *data.uid }, *data.time, - Some(data.stats), + dest_info, + Some(data.mass), )), }); }, diff --git a/common/systems/src/aura.rs b/common/systems/src/aura.rs index 372ac93e42..02f51c9d9e 100644 --- a/common/systems/src/aura.rs +++ b/common/systems/src/aura.rs @@ -4,9 +4,9 @@ use common::{ combat, comp::{ aura::{AuraChange, AuraKey, AuraKind, AuraTarget, EnteredAuras}, - buff::{Buff, BuffCategory, BuffChange, BuffSource}, + buff::{Buff, BuffCategory, BuffChange, BuffSource, DestInfo}, group::Group, - Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, Stats, + Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Mass, Player, Pos, Stats, }, event::{AuraEvent, BuffEvent, EmitExt}, event_emitters, @@ -41,6 +41,7 @@ pub struct ReadData<'a> { buffs: ReadStorage<'a, Buffs>, auras: ReadStorage<'a, Auras>, entered_auras: ReadStorage<'a, EnteredAuras>, + masses: ReadStorage<'a, Mass>, } #[derive(Default)] @@ -86,80 +87,74 @@ impl<'a> System<'a> for Sys { read_data.healths.get(target)?, read_data.uids.get(target)?, read_data.entered_auras.get(target)?, - read_data.stats.get(target), )) }) }); - target_iter.for_each( - |(target, target_pos, health, target_uid, entered_auras, stats)| { - let target_buffs = match read_data.buffs.get(target) { - Some(buff) => buff, - None => return, + target_iter.for_each(|(target, target_pos, health, target_uid, entered_auras)| { + let target_buffs = match read_data.buffs.get(target) { + Some(buff) => buff, + None => return, + }; + + // Ensure entity is within the aura radius + if target_pos.0.distance_squared(pos.0) < aura.radius.powi(2) { + // Ensure the entity is in the group we want to target + let same_group = |uid: Uid| { + read_data + .id_maps + .uid_entity(uid) + .and_then(|e| read_data.groups.get(e)) + .map_or(false, |owner_group| { + Some(owner_group) == read_data.groups.get(target) + }) + || *target_uid == uid }; - // Ensure entity is within the aura radius - if target_pos.0.distance_squared(pos.0) < aura.radius.powi(2) { - // Ensure the entity is in the group we want to target - let same_group = |uid: Uid| { - read_data - .id_maps - .uid_entity(uid) - .and_then(|e| read_data.groups.get(e)) - .map_or(false, |owner_group| { - Some(owner_group) == read_data.groups.get(target) - }) - || *target_uid == uid - }; + let allow_friendly_fire = + combat::allow_friendly_fire(&read_data.entered_auras, entity, target); - let allow_friendly_fire = combat::allow_friendly_fire( - &read_data.entered_auras, - entity, - target, - ); - - if !(allow_friendly_fire && entity != target - || match aura.target { - AuraTarget::GroupOf(uid) => same_group(uid), - AuraTarget::NotGroupOf(uid) => !same_group(uid), - AuraTarget::All => true, - }) - { - return; - } - - let did_activate = activate_aura( - key, - aura, - *uid, - target, - health, - target_buffs, - stats, - allow_friendly_fire, - &read_data, - &mut emitters, - ); - - if did_activate { - if entered_auras - .auras - .get(aura.aura_kind.as_ref()) - .map_or(true, |auras| !auras.contains(&(*uid, key))) - { - emitters.emit(AuraEvent { - entity: target, - aura_change: AuraChange::EnterAura( - *uid, - key, - *aura.aura_kind.as_ref(), - ), - }); - } - active_auras.insert((*uid, *target_uid, key)); - } + if !(allow_friendly_fire && entity != target + || match aura.target { + AuraTarget::GroupOf(uid) => same_group(uid), + AuraTarget::NotGroupOf(uid) => !same_group(uid), + AuraTarget::All => true, + }) + { + return; } - }, - ); + + let did_activate = activate_aura( + key, + aura, + entity, + *uid, + target, + health, + target_buffs, + allow_friendly_fire, + &read_data, + &mut emitters, + ); + + if did_activate { + if entered_auras + .auras + .get(aura.aura_kind.as_ref()) + .map_or(true, |auras| !auras.contains(&(*uid, key))) + { + emitters.emit(AuraEvent { + entity: target, + aura_change: AuraChange::EnterAura( + *uid, + key, + *aura.aura_kind.as_ref(), + ), + }); + } + active_auras.insert((*uid, *target_uid, key)); + } + } + }); } if !expired_auras.is_empty() { emitters.emit(AuraEvent { @@ -200,11 +195,11 @@ impl<'a> System<'a> for Sys { fn activate_aura( key: AuraKey, aura: &Aura, - applier: Uid, + applier: EcsEntity, + applier_uid: Uid, target: EcsEntity, health: &Health, target_buffs: &Buffs, - stats: Option<&Stats>, allow_friendly_fire: bool, read_data: &ReadData, emitters: &mut impl EmitExt, @@ -286,20 +281,25 @@ fn activate_aura( let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| { buff.cat_ids .iter() - .any(|cat_id| matches!(cat_id, BuffCategory::FromActiveAura(uid, aura_key) if *aura_key == key && *uid == applier)) + .any(|cat_id| matches!(cat_id, BuffCategory::FromActiveAura(uid, aura_key) if *aura_key == key && *uid == applier_uid)) && buff.kind == kind && buff.data.strength >= data.strength }); if emit_buff { + let dest_info = DestInfo { + stats: read_data.stats.get(target), + mass: read_data.masses.get(target), + }; emitters.emit(BuffEvent { entity: target, buff_change: BuffChange::Add(Buff::new( kind, data, - vec![category, BuffCategory::FromActiveAura(applier, key)], + vec![category, BuffCategory::FromActiveAura(applier_uid, key)], source, *read_data.time, - stats, + dest_info, + read_data.masses.get(applier), )), }); } diff --git a/common/systems/src/beam.rs b/common/systems/src/beam.rs index 74cc61671e..9a415647fe 100644 --- a/common/systems/src/beam.rs +++ b/common/systems/src/beam.rs @@ -3,8 +3,8 @@ use common::{ comp::{ agent::{Sound, SoundKind}, aura::EnteredAuras, - Alignment, Beam, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori, - Player, Pos, Scale, Stats, + Alignment, Beam, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, + Mass, Ori, Player, Pos, Scale, Stats, }, event::{self, EmitExt, EventBus}, event_emitters, @@ -63,6 +63,7 @@ pub struct ReadData<'a> { entered_auras: ReadStorage<'a, EnteredAuras>, outcomes: Read<'a, EventBus>, events: ReadAttackEvents<'a>, + masses: ReadStorage<'a, Mass>, } /// This system is responsible for handling beams that heal or do damage @@ -230,6 +231,7 @@ impl<'a> System<'a> for Sys { combo: read_data.combos.get(entity), inventory: read_data.inventories.get(entity), stats: read_data.stats.get(entity), + mass: read_data.masses.get(entity), }); let target_info = TargetInfo { @@ -243,6 +245,7 @@ impl<'a> System<'a> for Sys { char_state: read_data.character_states.get(target), energy: read_data.energies.get(target), buffs: read_data.buffs.get(target), + mass: read_data.masses.get(target), }; let target_dodging = read_data @@ -263,7 +266,7 @@ impl<'a> System<'a> for Sys { let precision_from_flank = combat::precision_mult_from_flank( beam.bezier.ctrl - beam.bezier.start, target_info.ori, - 1.0, + Default::default(), false, ); diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index 2a0ef88ca5..6b50e65335 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -6,12 +6,12 @@ use common::{ body::{object, Body}, buff::{ Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffKey, BuffKind, BuffSource, - Buffs, + Buffs, DestInfo, }, fluid_dynamics::{Fluid, LiquidKind}, item::MaterialStatManifest, - Alignment, Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind, - PhysicsState, Player, Pos, Stats, + Alignment, Energy, Group, Health, HealthChange, Inventory, LightEmitter, Mass, + ModifierKind, PhysicsState, Player, Pos, Stats, }, event::{ BuffEvent, ChangeBodyEvent, CreateSpriteEvent, EmitExt, EnergyChangeEvent, @@ -68,6 +68,7 @@ pub struct ReadData<'a> { alignments: ReadStorage<'a, Alignment>, players: ReadStorage<'a, Player>, uids: ReadStorage<'a, Uid>, + masses: ReadStorage<'a, Mass>, } #[derive(Default)] @@ -153,10 +154,16 @@ impl<'a> System<'a> for Sys { &read_data.energies, read_data.uids.maybe(), read_data.physics_states.maybe(), + read_data.masses.maybe(), ) .lend_join(); buff_join.for_each(|comps| { - let (entity, buff_comp, mut stat, body, health, energy, uid, physics_state) = comps; + let (entity, buff_comp, mut stat, body, health, energy, uid, physics_state, mass) = + comps; + let dest_info = DestInfo { + stats: Some(&stat), + mass, + }; // Apply buffs to entity based off of their current physics_state if let Some(physics_state) = physics_state { // Set nearby entities on fire if burning @@ -185,7 +192,13 @@ impl<'a> System<'a> for Sys { vec![BuffCategory::Natural], source, *read_data.time, - None, + DestInfo { + // Can't mutably access stats, and for burning debuff stats + // has no effect (for now) + stats: None, + mass: read_data.masses.get(t_entity), + }, + mass, )), }); } @@ -204,7 +217,8 @@ impl<'a> System<'a> for Sys { Vec::new(), BuffSource::World, *read_data.time, - Some(&stat), + dest_info, + None, )), }); } @@ -221,7 +235,8 @@ impl<'a> System<'a> for Sys { Vec::new(), BuffSource::World, *read_data.time, - Some(&stat), + dest_info, + None, )), }); } @@ -251,7 +266,8 @@ impl<'a> System<'a> for Sys { Vec::new(), BuffSource::World, *read_data.time, - Some(&stat), + dest_info, + None, )), }); } @@ -269,7 +285,8 @@ impl<'a> System<'a> for Sys { Vec::new(), BuffSource::World, *read_data.time, - Some(&stat), + dest_info, + None, )), }); } @@ -286,7 +303,8 @@ impl<'a> System<'a> for Sys { Vec::new(), BuffSource::World, *read_data.time, - Some(&stat), + dest_info, + None, )), }); } @@ -303,7 +321,8 @@ impl<'a> System<'a> for Sys { Vec::new(), BuffSource::World, *read_data.time, - Some(&stat), + dest_info, + None, )), }); // When standing on IceSpike also apply Frozen @@ -315,7 +334,8 @@ impl<'a> System<'a> for Sys { Vec::new(), BuffSource::World, *read_data.time, - Some(&stat), + dest_info, + None, )), }); } @@ -332,7 +352,8 @@ impl<'a> System<'a> for Sys { Vec::new(), BuffSource::World, *read_data.time, - Some(&stat), + dest_info, + None, )), }); } @@ -353,7 +374,8 @@ impl<'a> System<'a> for Sys { vec![BuffCategory::Natural], BuffSource::World, *read_data.time, - Some(&stat), + dest_info, + None, )), }); } else if matches!( @@ -429,7 +451,8 @@ impl<'a> System<'a> for Sys { .collect::>(), buff.source, *read_data.time, - Some(&stat), + dest_info, + None, )), }); } diff --git a/common/systems/src/melee.rs b/common/systems/src/melee.rs index 215e21b954..e37e268999 100644 --- a/common/systems/src/melee.rs +++ b/common/systems/src/melee.rs @@ -4,8 +4,8 @@ use common::{ agent::{Sound, SoundKind}, aura::EnteredAuras, melee::MultiTarget, - Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, - Ori, Player, Pos, Scale, Stats, + Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Mass, + Melee, Ori, Player, Pos, Scale, Stats, }, event::{self, EmitExt, EventBus}, event_emitters, @@ -62,6 +62,7 @@ pub struct ReadData<'a> { buffs: ReadStorage<'a, Buffs>, entered_auras: ReadStorage<'a, EnteredAuras>, events: ReadAttackEvents<'a>, + masses: ReadStorage<'a, Mass>, } /// This system is responsible for handling accepted inputs like moving or @@ -213,6 +214,7 @@ impl<'a> System<'a> for Sys { combo: read_data.combos.get(attacker), inventory: read_data.inventories.get(attacker), stats: read_data.stats.get(attacker), + mass: read_data.masses.get(attacker), }); let target_ori = read_data.orientations.get(target); @@ -228,6 +230,7 @@ impl<'a> System<'a> for Sys { char_state: target_char_state, energy: read_data.energies.get(target), buffs: read_data.buffs.get(target), + mass: read_data.masses.get(target), }; // PvP check @@ -248,7 +251,7 @@ impl<'a> System<'a> for Sys { .try_normalized() .unwrap_or(ori.look_vec()), target_ori, - melee_attack.precision_flank_multiplier, + melee_attack.precision_flank_multipliers, melee_attack.precision_flank_invert, ); diff --git a/common/systems/src/projectile.rs b/common/systems/src/projectile.rs index 55741cc5cd..c69b5ee98d 100644 --- a/common/systems/src/projectile.rs +++ b/common/systems/src/projectile.rs @@ -4,7 +4,7 @@ use common::{ agent::{Sound, SoundKind}, aura::EnteredAuras, projectile, Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, - Inventory, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel, + Inventory, Mass, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel, }, event::{ BonkEvent, BuffEvent, ComboChangeEvent, DeleteEvent, EmitExt, Emitter, EnergyChangeEvent, @@ -73,6 +73,7 @@ pub struct ReadData<'a> { terrain: ReadExpect<'a, TerrainGrid>, buffs: ReadStorage<'a, Buffs>, entered_auras: ReadStorage<'a, EnteredAuras>, + masses: ReadStorage<'a, Mass>, } /// This system is responsible for handling projectile effect triggers @@ -340,6 +341,7 @@ fn dispatch_hit( combo: read_data.combos.get(entity), inventory: read_data.inventories.get(entity), stats: read_data.stats.get(entity), + mass: read_data.masses.get(entity), }); let target_info = TargetInfo { @@ -353,6 +355,7 @@ fn dispatch_hit( char_state: read_data.character_states.get(target), energy: read_data.energies.get(target), buffs: read_data.buffs.get(target), + mass: read_data.masses.get(target), }; // TODO: Is it possible to have projectile without body?? @@ -389,8 +392,12 @@ fn dispatch_hit( .and_then(|cs| cs.attack_immunities()) .map_or(false, |i| i.projectiles); - let precision_from_flank = - combat::precision_mult_from_flank(*projectile_dir, target_info.ori, 1.0, false); + let precision_from_flank = combat::precision_mult_from_flank( + *projectile_dir, + target_info.ori, + Default::default(), + false, + ); let precision_from_head = { // This performs a cylinder and line segment intersection check. The cylinder is diff --git a/common/systems/src/shockwave.rs b/common/systems/src/shockwave.rs index 5aac5b350f..cd39b90138 100644 --- a/common/systems/src/shockwave.rs +++ b/common/systems/src/shockwave.rs @@ -4,7 +4,7 @@ use common::{ agent::{Sound, SoundKind}, aura::EnteredAuras, shockwave::ShockwaveDodgeable, - Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori, + Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Mass, Ori, PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats, }, event::{ @@ -64,6 +64,7 @@ pub struct ReadData<'a> { character_states: ReadStorage<'a, CharacterState>, buffs: ReadStorage<'a, Buffs>, entered_auras: ReadStorage<'a, EnteredAuras>, + masses: ReadStorage<'a, Mass>, } /// This system is responsible for handling accepted inputs like moving or @@ -233,6 +234,7 @@ impl<'a> System<'a> for Sys { combo: read_data.combos.get(entity), inventory: read_data.inventories.get(entity), stats: read_data.stats.get(entity), + mass: read_data.masses.get(entity), }); let target_info = TargetInfo { @@ -246,6 +248,7 @@ impl<'a> System<'a> for Sys { char_state: read_data.character_states.get(target), energy: read_data.energies.get(target), buffs: read_data.buffs.get(target), + mass: read_data.masses.get(target), }; let target_dodging = read_data diff --git a/server/src/cmd.rs b/server/src/cmd.rs index c1c6dffa84..f1ff31ae0a 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -30,7 +30,7 @@ use common::{ comp::{ self, aura::{AuraKindVariant, AuraTarget}, - buff::{Buff, BuffData, BuffKind, BuffSource, MiscBuffData}, + buff::{Buff, BuffData, BuffKind, BuffSource, DestInfo, MiscBuffData}, inventory::{ item::{all_items_expect, tool::AbilityMap, MaterialStatManifest, Quality}, slot::Slot, @@ -4552,7 +4552,8 @@ fn build_buff( | BuffKind::PotionSickness | BuffKind::Heatstroke | BuffKind::ScornfulTaunt - | BuffKind::Rooted => { + | BuffKind::Rooted + | BuffKind::Winded => { if buff_kind.is_simple() { unreachable!("is_simple() above") } else { @@ -4569,8 +4570,13 @@ fn cast_buff(buffkind: BuffKind, data: BuffData, server: &mut Server, target: Ec let ecs = &server.state.ecs(); let mut buffs_all = ecs.write_storage::(); let stats = ecs.read_storage::(); + let masses = ecs.read_storage::(); let time = ecs.read_resource::