From 78a2459e6de5ab389840531a912ac35af389c11f Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 25 Jan 2021 17:46:43 -0500 Subject: [PATCH 01/23] Changed knockback from an enum to a struct. --- .../common/abilities/staff/fireshockwave.ron | 2 +- .../unique/stonegolemfist/shockwave.ron | 2 +- common/src/combat.rs | 37 ++++++++++--------- common/src/comp/projectile.rs | 7 +++- common/src/lib.rs | 2 +- common/src/states/basic_melee.rs | 7 +++- common/src/states/charged_melee.rs | 7 +++- common/src/states/charged_ranged.rs | 7 +++- common/src/states/combo_melee.rs | 9 +++-- common/src/states/dash_melee.rs | 7 +++- common/src/states/leap_melee.rs | 7 +++- common/src/states/spin_melee.rs | 7 +++- 12 files changed, 62 insertions(+), 39 deletions(-) diff --git a/assets/common/abilities/staff/fireshockwave.ron b/assets/common/abilities/staff/fireshockwave.ron index 1a3c2c48bc..76631a94e6 100644 --- a/assets/common/abilities/staff/fireshockwave.ron +++ b/assets/common/abilities/staff/fireshockwave.ron @@ -5,7 +5,7 @@ Shockwave( recover_duration: 300, damage: 200, poise_damage: 0, - knockback: Away(25.0), + knockback: ( strength: 25.0, direction: Away), shockwave_angle: 360.0, shockwave_vertical_angle: 90.0, shockwave_speed: 20.0, diff --git a/assets/common/abilities/unique/stonegolemfist/shockwave.ron b/assets/common/abilities/unique/stonegolemfist/shockwave.ron index 7945e5d9c3..791274dee7 100644 --- a/assets/common/abilities/unique/stonegolemfist/shockwave.ron +++ b/assets/common/abilities/unique/stonegolemfist/shockwave.ron @@ -5,7 +5,7 @@ Shockwave( recover_duration: 800, damage: 500, poise_damage: 0, - knockback: TowardsUp(40.0), + knockback: (strength: 40.0, direction: TowardsUp), shockwave_angle: 90.0, shockwave_vertical_angle: 90.0, shockwave_speed: 50.0, diff --git a/common/src/combat.rs b/common/src/combat.rs index eb905c135f..ef9ca78189 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -181,34 +181,35 @@ impl Damage { } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum Knockback { - Away(f32), - Towards(f32), - Up(f32), - TowardsUp(f32), +pub struct Knockback { + pub direction: KnockbackDir, + pub strength: f32, +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum KnockbackDir { + Away, + Towards, + Up, + TowardsUp, } impl Knockback { pub fn calculate_impulse(self, dir: Dir) -> Vec3 { - match self { - Knockback::Away(strength) => strength * *Dir::slerp(dir, Dir::new(Vec3::unit_z()), 0.5), - Knockback::Towards(strength) => { - strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.5) + match self.direction { + KnockbackDir::Away => self.strength * *Dir::slerp(dir, Dir::new(Vec3::unit_z()), 0.5), + KnockbackDir::Towards => { + self.strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.5) }, - Knockback::Up(strength) => strength * Vec3::unit_z(), - Knockback::TowardsUp(strength) => { - strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.85) + KnockbackDir::Up => self.strength * Vec3::unit_z(), + KnockbackDir::TowardsUp => { + self.strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.85) }, } } pub fn modify_strength(mut self, power: f32) -> Self { - use Knockback::*; - match self { - Away(ref mut f) | Towards(ref mut f) | Up(ref mut f) | TowardsUp(ref mut f) => { - *f *= power; - }, - } + self.strength *= power; self } } diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 60c9a2b463..a49320ffb8 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -2,7 +2,7 @@ use crate::{ comp::buff::{BuffCategory, BuffData, BuffKind}, effect::{self, BuffEffect}, uid::Uid, - Damage, DamageSource, Explosion, GroupTarget, Knockback, RadiusEffect, + Damage, DamageSource, Explosion, GroupTarget, Knockback, KnockbackDir, RadiusEffect, }; use serde::{Deserialize, Serialize}; use specs::Component; @@ -89,7 +89,10 @@ impl ProjectileConstructor { source: DamageSource::Projectile, value: damage, }), - Effect::Knockback(Knockback::Away(knockback)), + Effect::Knockback(Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }), Effect::RewardEnergy(energy_regen), Effect::Vanish, Effect::Buff { diff --git a/common/src/lib.rs b/common/src/lib.rs index 83e93fbccf..b9114ff0c1 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -55,7 +55,7 @@ pub mod util; pub mod vol; pub mod volumes; -pub use combat::{Damage, DamageSource, GroupTarget, Knockback}; +pub use combat::{Damage, DamageSource, GroupTarget, Knockback, KnockbackDir}; pub use comp::inventory::loadout_builder::LoadoutBuilder; pub use explosion::{Explosion, RadiusEffect}; pub use skillset_builder::SkillSetBuilder; diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 42cb659afa..1f53363f08 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -7,7 +7,7 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, - Damage, DamageSource, GroupTarget, Knockback, + Damage, DamageSource, GroupTarget, Knockback, KnockbackDir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -109,7 +109,10 @@ impl CharacterBehavior for Data { max_angle: 180_f32.to_radians(), applied: false, hit_count: 0, - knockback: Knockback::Away(self.static_data.knockback), + knockback: Knockback { + strength: self.static_data.knockback, + direction: KnockbackDir::Away, + }, }); } else if self.timer < self.static_data.swing_duration { // Swings diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 23648ea051..a353deb64a 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -7,7 +7,7 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, }, - Damage, DamageSource, GroupTarget, Knockback, + Damage, DamageSource, GroupTarget, Knockback, KnockbackDir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -176,7 +176,10 @@ impl CharacterBehavior for Data { max_angle: self.static_data.max_angle.to_radians(), applied: false, hit_count: 0, - knockback: Knockback::Away(knockback), + knockback: Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }, }); } else if self.timer < self.static_data.swing_duration { // Swings diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 90c0ec468b..db86dad67d 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -10,7 +10,7 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, - Damage, DamageSource, GroupTarget, Knockback, + Damage, DamageSource, GroupTarget, Knockback, KnockbackDir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -114,7 +114,10 @@ impl CharacterBehavior for Data { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ projectile::Effect::Damage(Some(GroupTarget::OutOfGroup), damage), - projectile::Effect::Knockback(Knockback::Away(knockback)), + projectile::Effect::Knockback(Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }), projectile::Effect::Vanish, projectile::Effect::Buff { buff: BuffEffect { diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index da4f080fcf..5ef656b8bf 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -7,7 +7,7 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, - Damage, DamageSource, GroupTarget, Knockback, + Damage, DamageSource, GroupTarget, Knockback, KnockbackDir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -197,9 +197,10 @@ impl CharacterBehavior for Data { max_angle: self.static_data.stage_data[stage_index].angle.to_radians(), applied: false, hit_count: 0, - knockback: Knockback::Away( - self.static_data.stage_data[stage_index].knockback, - ), + knockback: Knockback { + strength: self.static_data.stage_data[stage_index].knockback, + direction: KnockbackDir::Away, + }, }); } }, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 46fa3c1011..df1955963e 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -7,7 +7,7 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, - Damage, DamageSource, GroupTarget, Knockback, + Damage, DamageSource, GroupTarget, Knockback, KnockbackDir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -155,7 +155,10 @@ impl CharacterBehavior for Data { max_angle: self.static_data.angle.to_radians(), applied: false, hit_count: 0, - knockback: Knockback::Away(knockback), + knockback: Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }, }); } update.character = CharacterState::DashMelee(Data { diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 6afabfb583..76f36c63a9 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -4,7 +4,7 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, }, - Damage, DamageSource, GroupTarget, Knockback, + Damage, DamageSource, GroupTarget, Knockback, KnockbackDir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -164,7 +164,10 @@ impl CharacterBehavior for Data { max_angle: self.static_data.max_angle.to_radians(), applied: false, hit_count: 0, - knockback: Knockback::Away(self.static_data.knockback), + knockback: Knockback { + strength: self.static_data.knockback, + direction: KnockbackDir::Away, + }, }); update.character = CharacterState::LeapMelee(Data { diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 12072b3484..3a1305156c 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -8,7 +8,7 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, - Damage, DamageSource, GroupTarget, Knockback, + Damage, DamageSource, GroupTarget, Knockback, KnockbackDir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -131,7 +131,10 @@ impl CharacterBehavior for Data { max_angle: 180_f32.to_radians(), applied: false, hit_count: 0, - knockback: Knockback::Away(self.static_data.knockback), + knockback: Knockback { + strength: self.static_data.knockback, + direction: KnockbackDir::Away, + }, }); } else if self.timer < self.static_data.swing_duration { if matches!( From bb019309ea2d9c058145fbad873759efce66d5b1 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 25 Jan 2021 19:01:07 -0500 Subject: [PATCH 02/23] Attack builder implemented. --- common/src/combat.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/common/src/combat.rs b/common/src/combat.rs index ef9ca78189..f0738fbea9 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -23,6 +23,85 @@ pub enum GroupTarget { OutOfGroup, } +pub struct Attack { + damages: Vec, + effects: Vec, + crit_chance: f32, + crit_multiplier: f32, +} + +impl Default for Attack { + fn default() -> Self { + Self { + damages: Vec::new(), + effects: Vec::new(), + crit_chance: 0.0, + crit_multiplier: 1.0, + } + } +} + +impl Attack { + pub fn with_damage(mut self, damage: DamageComponent) -> Self { + self.damages.push(damage); + self + } + + pub fn with_effect(mut self, effect: EffectComponent) -> Self { + self.effects.push(effect); + self + } + + pub fn with_crit(mut self, cc: f32, cm: f32) -> Self { + self.crit_chance = cc; + self.crit_multiplier = cm; + self + } +} + +pub struct DamageComponent { + damage: Damage, + target: Option, + effects: Vec, +} + +impl DamageComponent { + pub fn new(damage: Damage, target: Option) -> Self { + Self { + damage, + target, + effects: Vec::new(), + } + } + + pub fn with_effect(mut self, effect: AttackEffect) -> Self { + self.effects.push(effect); + self + } +} + +pub struct EffectComponent { + target: Option, + effect: AttackEffect, +} + +impl EffectComponent { + pub fn new(target: Option, effect: AttackEffect) -> Self { + Self { + target, + effect, + } + } +} + +pub enum AttackEffect { + Heal, + Buff, + Knockback, + EnergyChange, + Lifesteal, +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum DamageSource { Buff(BuffKind), From d3b75df76f4ff29656c41139ed45a142c9426b7a Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 25 Jan 2021 19:31:06 -0500 Subject: [PATCH 03/23] Changed name of Attacking comp to MeleeAttack --- common/src/combat.rs | 16 +++++++--------- common/src/comp/character_state.rs | 4 ++-- common/src/comp/mod.rs | 2 +- common/src/states/basic_melee.rs | 12 ++++++------ common/src/states/behavior.rs | 10 +++++----- common/src/states/charged_melee.rs | 8 ++++---- common/src/states/combo_melee.rs | 12 ++++++------ common/src/states/dash_melee.rs | 8 ++++---- common/src/states/leap_melee.rs | 8 ++++---- common/src/states/spin_melee.rs | 8 ++++---- common/sys/src/character_behavior.rs | 4 ++-- common/sys/src/melee.rs | 4 ++-- common/sys/src/state.rs | 2 +- 13 files changed, 48 insertions(+), 50 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index f0738fbea9..366576cbb7 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -11,6 +11,7 @@ use crate::{ skills::{SkillGroupKind, SkillSet}, Body, BuffKind, Health, HealthChange, HealthSource, Inventory, Stats, }, + effect, uid::Uid, util::Dir, }; @@ -87,19 +88,16 @@ pub struct EffectComponent { impl EffectComponent { pub fn new(target: Option, effect: AttackEffect) -> Self { - Self { - target, - effect, - } + Self { target, effect } } } pub enum AttackEffect { - Heal, - Buff, - Knockback, - EnergyChange, - Lifesteal, + Heal(f32), + Buff(effect::BuffEffect), + Knockback(Knockback), + EnergyChange(f32), + Lifesteal(f32), } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 7f18e3b7d3..1ed8d903a9 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -170,7 +170,7 @@ impl Component for CharacterState { } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Attacking { +pub struct MeleeAttack { pub effects: Vec<(Option, Damage, PoiseChange)>, pub range: f32, pub max_angle: f32, @@ -179,6 +179,6 @@ pub struct Attacking { pub knockback: Knockback, } -impl Component for Attacking { +impl Component for MeleeAttack { type Storage = VecStorage; } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 5bfa41ae23..76e4438573 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -40,7 +40,7 @@ pub use buff::{ Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs, ModifierKind, }; -pub use character_state::{Attacking, CharacterState, StateUpdate}; +pub use character_state::{CharacterState, MeleeAttack, StateUpdate}; pub use chat::{ ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg, }; diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 1f53363f08..d64aeeeb87 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -1,6 +1,6 @@ use crate::{ comp::{ - Attacking, CharacterState, EnergyChange, EnergySource, PoiseChange, PoiseSource, + CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, }, states::{ @@ -93,7 +93,7 @@ impl CharacterBehavior for Data { }); // Hit attempt - data.updater.insert(data.entity, Attacking { + data.updater.insert(data.entity, MeleeAttack { effects: vec![( Some(GroupTarget::OutOfGroup), Damage { @@ -146,21 +146,21 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } // Grant energy on successful hit - if let Some(attack) = data.attacking { + if let Some(attack) = data.melee_attack { if attack.applied && attack.hit_count > 0 { - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); update.energy.change_by(EnergyChange { amount: 50, source: EnergySource::HitEnemy, diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index e1479da487..c6c66df258 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - Attacking, Beam, Body, CharacterState, ControlAction, Controller, ControllerInputs, Energy, - Health, Inventory, Ori, PhysicsState, Pos, StateUpdate, Stats, Vel, + Beam, Body, CharacterState, ControlAction, Controller, ControllerInputs, Energy, Health, + Inventory, MeleeAttack, Ori, PhysicsState, Pos, StateUpdate, Stats, Vel, }, resources::DeltaTime, uid::Uid, @@ -57,7 +57,7 @@ pub struct JoinData<'a> { pub inventory: &'a Inventory, pub body: &'a Body, pub physics: &'a PhysicsState, - pub attacking: Option<&'a Attacking>, + pub melee_attack: Option<&'a MeleeAttack>, pub updater: &'a LazyUpdate, pub stats: &'a Stats, } @@ -84,7 +84,7 @@ pub type JoinTuple<'a> = ( &'a Health, &'a Body, &'a PhysicsState, - Option<&'a Attacking>, + Option<&'a MeleeAttack>, Option<&'a Beam>, &'a Stats, ); @@ -105,7 +105,7 @@ impl<'a> JoinData<'a> { health: j.9, body: j.10, physics: j.11, - attacking: j.12, + melee_attack: j.12, stats: j.14, updater, dt, diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index a353deb64a..a56426ee7e 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -1,6 +1,6 @@ use crate::{ comp::{ - Attacking, CharacterState, EnergyChange, EnergySource, PoiseChange, PoiseSource, + CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, }, states::{ @@ -170,7 +170,7 @@ impl CharacterBehavior for Data { + self.charge_amount * self.static_data.scaled_knockback; // Hit attempt - data.updater.insert(data.entity, Attacking { + data.updater.insert(data.entity, MeleeAttack { effects: vec![(Some(GroupTarget::OutOfGroup), damage, poise_damage)], range: self.static_data.range, max_angle: self.static_data.max_angle.to_radians(), @@ -213,14 +213,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 5ef656b8bf..eb01017eab 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -1,6 +1,6 @@ use crate::{ comp::{ - Attacking, CharacterState, EnergyChange, EnergySource, PoiseChange, PoiseSource, + CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, }, states::{ @@ -181,7 +181,7 @@ impl CharacterBehavior for Data { .scales_from_combo .min(self.combo / self.static_data.num_stages) * self.static_data.stage_data[stage_index].poise_damage_increase; - data.updater.insert(data.entity, Attacking { + data.updater.insert(data.entity, MeleeAttack { effects: vec![( Some(GroupTarget::OutOfGroup), Damage { @@ -273,19 +273,19 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } // Grant energy on successful hit - if let Some(attack) = data.attacking { + if let Some(attack) = data.melee_attack { if attack.applied && attack.hit_count > 0 { let energy = self.static_data.max_energy_gain.min( self.static_data.initial_energy_gain @@ -299,7 +299,7 @@ impl CharacterBehavior for Data { stage_section: self.stage_section, next_stage: self.next_stage, }); - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); update.energy.change_by(EnergyChange { amount: energy, source: EnergySource::HitEnemy, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index df1955963e..6787ee8999 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -1,6 +1,6 @@ use crate::{ comp::{ - Attacking, CharacterState, EnergyChange, EnergySource, PoiseChange, PoiseSource, + CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, }, states::{ @@ -145,7 +145,7 @@ impl CharacterBehavior for Data { }; let knockback = self.static_data.base_knockback + charge_frac * self.static_data.scaled_knockback; - data.updater.insert(data.entity, Attacking { + data.updater.insert(data.entity, MeleeAttack { effects: vec![( Some(GroupTarget::OutOfGroup), damage, @@ -238,14 +238,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 76f36c63a9..87cd1fda7b 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{Attacking, CharacterState, PoiseChange, PoiseSource, StateUpdate}, + comp::{CharacterState, MeleeAttack, PoiseChange, PoiseSource, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, @@ -148,7 +148,7 @@ impl CharacterBehavior for Data { StageSection::Recover => { if !self.exhausted { // Hit attempt, when animation plays - data.updater.insert(data.entity, Attacking { + data.updater.insert(data.entity, MeleeAttack { effects: vec![( Some(GroupTarget::OutOfGroup), Damage { @@ -191,14 +191,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 3a1305156c..d311d7d7e4 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -1,6 +1,6 @@ use crate::{ comp::{ - Attacking, CharacterState, EnergyChange, EnergySource, PoiseChange, PoiseSource, + CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, }, consts::GRAVITY, @@ -115,7 +115,7 @@ impl CharacterBehavior for Data { ..*self }); // Hit attempt - data.updater.insert(data.entity, Attacking { + data.updater.insert(data.entity, MeleeAttack { effects: vec![( Some(GroupTarget::OutOfGroup), Damage { @@ -204,14 +204,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } diff --git a/common/sys/src/character_behavior.rs b/common/sys/src/character_behavior.rs index 86f760f620..8802ed7dba 100644 --- a/common/sys/src/character_behavior.rs +++ b/common/sys/src/character_behavior.rs @@ -3,7 +3,7 @@ use specs::{Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, System, W use common::{ comp::{ inventory::slot::{EquipSlot, Slot}, - Attacking, Beam, Body, CharacterState, Controller, Energy, Health, Inventory, Mounting, + Beam, Body, CharacterState, Controller, Energy, Health, Inventory, MeleeAttack, Mounting, Ori, PhysicsState, Poise, PoiseState, Pos, StateUpdate, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, @@ -69,7 +69,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Poise>, ReadStorage<'a, Body>, ReadStorage<'a, PhysicsState>, - ReadStorage<'a, Attacking>, + ReadStorage<'a, MeleeAttack>, ReadStorage<'a, Beam>, ReadStorage<'a, Uid>, ReadStorage<'a, Mounting>, diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index f1c2810c0a..c06f1fce45 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -1,5 +1,5 @@ use common::{ - comp::{buff, group, Attacking, Body, CharacterState, Health, Inventory, Ori, Pos, Scale}, + comp::{buff, group, Body, CharacterState, Health, Inventory, MeleeAttack, Ori, Pos, Scale}, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, span, @@ -30,7 +30,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Health>, ReadStorage<'a, Inventory>, ReadStorage<'a, group::Group>, - WriteStorage<'a, Attacking>, + WriteStorage<'a, MeleeAttack>, ReadStorage<'a, CharacterState>, ); diff --git a/common/sys/src/state.rs b/common/sys/src/state.rs index c5ba883d4c..9dea038ee0 100644 --- a/common/sys/src/state.rs +++ b/common/sys/src/state.rs @@ -163,7 +163,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); - ecs.register::(); + ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); From 2690e9caa1ef89a8e97b4e174665c6b4391a3f0d Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 25 Jan 2021 21:50:16 -0500 Subject: [PATCH 04/23] Applied basic functionality of attack-effects system to melee. --- common/src/combat.rs | 36 +++++++++++++++++++---- common/src/comp/character_state.rs | 7 ++--- common/src/states/basic_melee.rs | 31 ++++++++++---------- common/src/states/charged_melee.rs | 14 +++++---- common/src/states/combo_melee.rs | 30 +++++++++---------- common/src/states/dash_melee.rs | 20 +++++++------ common/src/states/leap_melee.rs | 29 +++++++++---------- common/src/states/spin_melee.rs | 30 +++++++++---------- common/sys/src/lib.rs | 46 +++++++++++++++++++++++++++++- common/sys/src/melee.rs | 23 +++++++++++++-- 10 files changed, 177 insertions(+), 89 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 366576cbb7..2f15ba9600 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -24,11 +24,12 @@ pub enum GroupTarget { OutOfGroup, } +#[derive(Debug, Serialize, Deserialize)] pub struct Attack { damages: Vec, effects: Vec, - crit_chance: f32, - crit_multiplier: f32, + pub crit_chance: f32, + pub crit_multiplier: f32, } impl Default for Attack { @@ -58,8 +59,17 @@ impl Attack { self.crit_multiplier = cm; self } + + pub fn damages(&self) -> impl Iterator { + self.damages.iter() + } + + pub fn effects(&self) -> impl Iterator { + self.effects.iter() + } } +#[derive(Debug, Serialize, Deserialize)] pub struct DamageComponent { damage: Damage, target: Option, @@ -79,8 +89,21 @@ impl DamageComponent { self.effects.push(effect); self } + + pub fn target(&self) -> Option { + self.target + } + + pub fn damage(&self) -> Damage { + self.damage + } + + pub fn effects(&self) -> impl Iterator { + self.effects.iter() + } } +#[derive(Debug, Serialize, Deserialize)] pub struct EffectComponent { target: Option, effect: AttackEffect, @@ -92,12 +115,13 @@ impl EffectComponent { } } +#[derive(Debug, Serialize, Deserialize)] pub enum AttackEffect { - Heal(f32), - Buff(effect::BuffEffect), + //Heal(f32), + //Buff(effect::BuffEffect), Knockback(Knockback), - EnergyChange(f32), - Lifesteal(f32), + /*EnergyChange(f32), + *Lifesteal(f32), */ } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 1ed8d903a9..5c6711edf4 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -1,8 +1,8 @@ use crate::{ + combat::Attack, comp::{Energy, Ori, PoiseChange, Pos, Vel}, event::{LocalEvent, ServerEvent}, states::{behavior::JoinData, *}, - Damage, GroupTarget, Knockback, }; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage, VecStorage}; @@ -169,14 +169,13 @@ impl Component for CharacterState { type Storage = DerefFlaggedStorage>; } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct MeleeAttack { - pub effects: Vec<(Option, Damage, PoiseChange)>, + pub attack: Attack, pub range: f32, pub max_angle: f32, pub applied: bool, pub hit_count: u32, - pub knockback: Knockback, } impl Component for MeleeAttack { diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index d64aeeeb87..e28a5d3e39 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -1,4 +1,5 @@ use crate::{ + combat::{Attack, AttackEffect, DamageComponent}, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -92,27 +93,25 @@ impl CharacterBehavior for Data { ..*self }); + let damage = Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }; + let knockback = AttackEffect::Knockback(Knockback { + strength: self.static_data.knockback, + direction: KnockbackDir::Away, + }); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(knockback); + let attack = Attack::default().with_damage(damage); + // Hit attempt data.updater.insert(data.entity, MeleeAttack { - effects: vec![( - Some(GroupTarget::OutOfGroup), - Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32, - }, - PoiseChange { - amount: -(self.static_data.base_poise_damage as i32), - source: PoiseSource::Attack, - }, - )], + attack, range: self.static_data.range, - max_angle: 180_f32.to_radians(), + max_angle: self.static_data.max_angle, applied: false, hit_count: 0, - knockback: Knockback { - strength: self.static_data.knockback, - direction: KnockbackDir::Away, - }, }); } else if self.timer < self.static_data.swing_duration { // Swings diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index a56426ee7e..3f974cd49b 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -1,4 +1,5 @@ use crate::{ + combat::{Attack, AttackEffect, DamageComponent}, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -168,18 +169,21 @@ impl CharacterBehavior for Data { }; let knockback = self.static_data.initial_knockback + self.charge_amount * self.static_data.scaled_knockback; + let knockback = AttackEffect::Knockback(Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(knockback); + let attack = Attack::default().with_damage(damage); // Hit attempt data.updater.insert(data.entity, MeleeAttack { - effects: vec![(Some(GroupTarget::OutOfGroup), damage, poise_damage)], + attack, range: self.static_data.range, max_angle: self.static_data.max_angle.to_radians(), applied: false, hit_count: 0, - knockback: Knockback { - strength: knockback, - direction: KnockbackDir::Away, - }, }); } else if self.timer < self.static_data.swing_duration { // Swings diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index eb01017eab..423c43c519 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -1,4 +1,5 @@ use crate::{ + combat::{Attack, AttackEffect, DamageComponent}, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -181,26 +182,25 @@ impl CharacterBehavior for Data { .scales_from_combo .min(self.combo / self.static_data.num_stages) * self.static_data.stage_data[stage_index].poise_damage_increase; + + let damage = Damage { + source: DamageSource::Melee, + value: damage as f32, + }; + let knockback = AttackEffect::Knockback(Knockback { + strength: self.static_data.stage_data[stage_index].knockback, + direction: KnockbackDir::Away, + }); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(knockback); + let attack = Attack::default().with_damage(damage); + data.updater.insert(data.entity, MeleeAttack { - effects: vec![( - Some(GroupTarget::OutOfGroup), - Damage { - source: DamageSource::Melee, - value: damage as f32, - }, - PoiseChange { - amount: -(poise_damage as i32), - source: PoiseSource::Attack, - }, - )], + attack, range: self.static_data.stage_data[stage_index].range, max_angle: self.static_data.stage_data[stage_index].angle.to_radians(), applied: false, hit_count: 0, - knockback: Knockback { - strength: self.static_data.stage_data[stage_index].knockback, - direction: KnockbackDir::Away, - }, }); } }, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 6787ee8999..efaf48de43 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -1,4 +1,5 @@ use crate::{ + combat::{Attack, AttackEffect, DamageComponent}, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -145,20 +146,21 @@ impl CharacterBehavior for Data { }; let knockback = self.static_data.base_knockback + charge_frac * self.static_data.scaled_knockback; + let knockback = AttackEffect::Knockback(Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }); + let damage = + DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(knockback); + let attack = Attack::default().with_damage(damage); + data.updater.insert(data.entity, MeleeAttack { - effects: vec![( - Some(GroupTarget::OutOfGroup), - damage, - poise_damage, - )], + attack, range: self.static_data.range, max_angle: self.static_data.angle.to_radians(), applied: false, hit_count: 0, - knockback: Knockback { - strength: knockback, - direction: KnockbackDir::Away, - }, }); } update.character = CharacterState::DashMelee(Data { diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 87cd1fda7b..52bbc97fb2 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -1,4 +1,5 @@ use crate::{ + combat::{Attack, AttackEffect, DamageComponent}, comp::{CharacterState, MeleeAttack, PoiseChange, PoiseSource, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, @@ -147,27 +148,25 @@ impl CharacterBehavior for Data { }, StageSection::Recover => { if !self.exhausted { + let damage = Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }; + let knockback = AttackEffect::Knockback(Knockback { + strength: self.static_data.knockback, + direction: KnockbackDir::Away, + }); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(knockback); + let attack = Attack::default().with_damage(damage); + // Hit attempt, when animation plays data.updater.insert(data.entity, MeleeAttack { - effects: vec![( - Some(GroupTarget::OutOfGroup), - Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32, - }, - PoiseChange { - amount: -(self.static_data.base_poise_damage as i32), - source: PoiseSource::Attack, - }, - )], + attack, range: self.static_data.range, max_angle: self.static_data.max_angle.to_radians(), applied: false, hit_count: 0, - knockback: Knockback { - strength: self.static_data.knockback, - direction: KnockbackDir::Away, - }, }); update.character = CharacterState::LeapMelee(Data { diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index d311d7d7e4..0716c8bafb 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -1,4 +1,5 @@ use crate::{ + combat::{Attack, AttackEffect, DamageComponent}, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -114,27 +115,26 @@ impl CharacterBehavior for Data { exhausted: true, ..*self }); + + let damage = Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }; + let knockback = AttackEffect::Knockback(Knockback { + strength: self.static_data.knockback, + direction: KnockbackDir::Away, + }); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(knockback); + let attack = Attack::default().with_damage(damage); + // Hit attempt data.updater.insert(data.entity, MeleeAttack { - effects: vec![( - Some(GroupTarget::OutOfGroup), - Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32, - }, - PoiseChange { - amount: -(self.static_data.base_poise_damage as i32), - source: PoiseSource::Attack, - }, - )], + attack, range: self.static_data.range, max_angle: 180_f32.to_radians(), applied: false, hit_count: 0, - knockback: Knockback { - strength: self.static_data.knockback, - direction: KnockbackDir::Away, - }, }); } else if self.timer < self.static_data.swing_duration { if matches!( diff --git a/common/sys/src/lib.rs b/common/sys/src/lib.rs index 88daef5fa5..c5e939288e 100644 --- a/common/sys/src/lib.rs +++ b/common/sys/src/lib.rs @@ -16,7 +16,15 @@ pub mod state; mod stats; // External -use specs::DispatcherBuilder; +use common::{ + combat::{Attack, AttackEffect, GroupTarget}, + comp::Inventory, + event::ServerEvent, + uid::Uid, + util::Dir, +}; +use rand::{thread_rng, Rng}; +use specs::{DispatcherBuilder, Entity as EcsEntity}; // System names pub const CHARACTER_BEHAVIOR_SYS: &str = "character_behavior_sys"; @@ -48,3 +56,39 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]); dispatch_builder.add(aura::Sys, AURAS_SYS, &[]); } + +pub fn apply_attack( + attack: &Attack, + target_group: GroupTarget, + target_entity: EcsEntity, + inventory: Option<&Inventory>, + uid: Uid, + dir: Dir, +) -> Vec { + let is_crit = thread_rng().gen::() < attack.crit_chance; + let mut accumulated_damage = 0.0; + let mut server_events = Vec::new(); + for damage in attack + .damages() + .filter(|d| d.target().map_or(true, |t| t == target_group)) + { + let change = damage.damage().modify_damage(inventory, Some(uid)); + if change.amount != 0 { + server_events.push(ServerEvent::Damage { entity: target_entity, change }); + for effect in damage.effects() { + match effect { + AttackEffect::Knockback(kb) => { + let impulse = kb.calculate_impulse(dir); + if !impulse.is_approx_zero() { + server_events.push(ServerEvent::Knockback { + entity: target_entity, + impulse, + }); + } + }, + } + } + } + } + server_events +} diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index c06f1fce45..139ccc7cd6 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -1,3 +1,4 @@ +use crate::apply_attack; use common::{ comp::{buff, group, Body, CharacterState, Health, Inventory, MeleeAttack, Ori, Pos, Scale}, event::{EventBus, LocalEvent, ServerEvent}, @@ -75,13 +76,14 @@ impl<'a> System<'a> for Sys { attack.applied = true; // Go through all other entities - for (b, pos_b, scale_b_maybe, health_b, body_b, char_state_b_maybe) in ( + for (b, pos_b, scale_b_maybe, health_b, body_b, char_state_b_maybe, inventory_b_maybe) in ( &entities, &positions, scales.maybe(), &healths, &bodies, char_states.maybe(), + inventories.maybe(), ) .join() { @@ -118,7 +120,22 @@ impl<'a> System<'a> for Sys { GroupTarget::OutOfGroup }; - for (target, damage, poise_change) in attack.effects.iter() { + let dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); + + let server_events = apply_attack( + &attack.attack, + target_group, + b, + inventory_b_maybe, + *uid, + dir, + ); + + for event in server_events { + server_emitter.emit(event); + } + + /*for (target, damage) in attack.damages.iter() { if let Some(target) = target { if *target != target_group || (!matches!(target, GroupTarget::InGroup) && is_dodge) @@ -165,7 +182,7 @@ impl<'a> System<'a> for Sys { } attack.hit_count += 1; - } + }*/ } } } From c3408c084c571253a61ab66271958b09f9f0cb07 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 25 Jan 2021 22:53:52 -0500 Subject: [PATCH 05/23] Critical hits are now functional. --- common/src/combat.rs | 82 ++++++++++++++++-------- common/src/states/basic_melee.rs | 2 +- common/src/states/charged_melee.rs | 2 +- common/src/states/combo_melee.rs | 4 +- common/src/states/dash_melee.rs | 2 +- common/src/states/leap_melee.rs | 2 +- common/src/states/spin_melee.rs | 2 +- common/sys/src/beam.rs | 8 ++- common/sys/src/lib.rs | 46 +------------ common/sys/src/melee.rs | 23 ++++--- common/sys/src/projectile.rs | 8 ++- common/sys/src/shockwave.rs | 3 +- server/src/events/entity_manipulation.rs | 2 +- server/src/state_ext.rs | 2 +- 14 files changed, 95 insertions(+), 93 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 2f15ba9600..daa923c8d6 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -12,10 +12,13 @@ use crate::{ Body, BuffKind, Health, HealthChange, HealthSource, Inventory, Stats, }, effect, + event::ServerEvent, uid::Uid, util::Dir, }; +use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; +use specs::Entity as EcsEntity; use vek::*; #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -28,8 +31,8 @@ pub enum GroupTarget { pub struct Attack { damages: Vec, effects: Vec, - pub crit_chance: f32, - pub crit_multiplier: f32, + crit_chance: f32, + crit_multiplier: f32, } impl Default for Attack { @@ -60,12 +63,47 @@ impl Attack { self } - pub fn damages(&self) -> impl Iterator { - self.damages.iter() - } - - pub fn effects(&self) -> impl Iterator { - self.effects.iter() + pub fn apply_attack( + &self, + target_group: GroupTarget, + target_entity: EcsEntity, + inventory: Option<&Inventory>, + uid: Uid, + dir: Dir, + ) -> Vec { + let is_crit = thread_rng().gen::() < self.crit_chance; + let mut accumulated_damage = 0.0; + let mut server_events = Vec::new(); + for damage in self + .damages + .iter() + .filter(|d| d.target.map_or(true, |t| t == target_group)) + { + let change = + damage + .damage + .modify_damage(inventory, Some(uid), is_crit, self.crit_multiplier); + if change.amount != 0 { + server_events.push(ServerEvent::Damage { + entity: target_entity, + change, + }); + for effect in damage.effects.iter() { + match effect { + AttackEffect::Knockback(kb) => { + let impulse = kb.calculate_impulse(dir); + if !impulse.is_approx_zero() { + server_events.push(ServerEvent::Knockback { + entity: target_entity, + impulse, + }); + } + }, + } + } + } + } + server_events } } @@ -89,18 +127,6 @@ impl DamageComponent { self.effects.push(effect); self } - - pub fn target(&self) -> Option { - self.target - } - - pub fn damage(&self) -> Damage { - self.damage - } - - pub fn effects(&self) -> impl Iterator { - self.effects.iter() - } } #[derive(Debug, Serialize, Deserialize)] @@ -166,15 +192,21 @@ impl Damage { } } - pub fn modify_damage(self, inventory: Option<&Inventory>, uid: Option) -> HealthChange { + pub fn modify_damage( + self, + inventory: Option<&Inventory>, + uid: Option, + is_crit: bool, + crit_mult: f32, + ) -> HealthChange { let mut damage = self.value; let damage_reduction = inventory.map_or(0.0, |inv| Damage::compute_damage_reduction(inv)); match self.source { DamageSource::Melee => { // Critical hit let mut critdamage = 0.0; - if rand::random() { - critdamage = damage * 0.3; + if is_crit { + critdamage = damage * (crit_mult - 1.0); } // Armor damage *= 1.0 - damage_reduction; @@ -194,8 +226,8 @@ impl Damage { }, DamageSource::Projectile => { // Critical hit - if rand::random() { - damage *= 1.2; + if is_crit { + damage *= crit_mult; } // Armor damage *= 1.0 - damage_reduction; diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index e28a5d3e39..e5c080bdee 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -103,7 +103,7 @@ impl CharacterBehavior for Data { }); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback); - let attack = Attack::default().with_damage(damage); + let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); // Hit attempt data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 3f974cd49b..592ecbdc22 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -175,7 +175,7 @@ impl CharacterBehavior for Data { }); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback); - let attack = Attack::default().with_damage(damage); + let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); // Hit attempt data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 423c43c519..b7913a8edf 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -193,8 +193,8 @@ impl CharacterBehavior for Data { }); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback); - let attack = Attack::default().with_damage(damage); - + let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); + data.updater.insert(data.entity, MeleeAttack { attack, range: self.static_data.stage_data[stage_index].range, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index efaf48de43..ca6677663d 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -153,7 +153,7 @@ impl CharacterBehavior for Data { let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback); - let attack = Attack::default().with_damage(damage); + let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); data.updater.insert(data.entity, MeleeAttack { attack, diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 52bbc97fb2..fae603386f 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -158,7 +158,7 @@ impl CharacterBehavior for Data { }); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback); - let attack = Attack::default().with_damage(damage); + let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); // Hit attempt, when animation plays data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 0716c8bafb..5b172bbf05 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -126,7 +126,7 @@ impl CharacterBehavior for Data { }); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback); - let attack = Attack::default().with_damage(damage); + let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); // Hit attempt data.updater.insert(data.entity, MeleeAttack { diff --git a/common/sys/src/beam.rs b/common/sys/src/beam.rs index d38699d7b9..1dc2041117 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -166,7 +166,13 @@ impl<'a> System<'a> for Sys { } // Modify damage - let change = damage.modify_damage(inventories.get(b), beam_segment.owner); + let change = damage.modify_damage( + inventories.get(b), + beam_segment.owner, + false, + 0.0, + ); + match target { Some(GroupTarget::OutOfGroup) => { server_emitter.emit(ServerEvent::Damage { entity: b, change }); diff --git a/common/sys/src/lib.rs b/common/sys/src/lib.rs index c5e939288e..88daef5fa5 100644 --- a/common/sys/src/lib.rs +++ b/common/sys/src/lib.rs @@ -16,15 +16,7 @@ pub mod state; mod stats; // External -use common::{ - combat::{Attack, AttackEffect, GroupTarget}, - comp::Inventory, - event::ServerEvent, - uid::Uid, - util::Dir, -}; -use rand::{thread_rng, Rng}; -use specs::{DispatcherBuilder, Entity as EcsEntity}; +use specs::DispatcherBuilder; // System names pub const CHARACTER_BEHAVIOR_SYS: &str = "character_behavior_sys"; @@ -56,39 +48,3 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]); dispatch_builder.add(aura::Sys, AURAS_SYS, &[]); } - -pub fn apply_attack( - attack: &Attack, - target_group: GroupTarget, - target_entity: EcsEntity, - inventory: Option<&Inventory>, - uid: Uid, - dir: Dir, -) -> Vec { - let is_crit = thread_rng().gen::() < attack.crit_chance; - let mut accumulated_damage = 0.0; - let mut server_events = Vec::new(); - for damage in attack - .damages() - .filter(|d| d.target().map_or(true, |t| t == target_group)) - { - let change = damage.damage().modify_damage(inventory, Some(uid)); - if change.amount != 0 { - server_events.push(ServerEvent::Damage { entity: target_entity, change }); - for effect in damage.effects() { - match effect { - AttackEffect::Knockback(kb) => { - let impulse = kb.calculate_impulse(dir); - if !impulse.is_approx_zero() { - server_events.push(ServerEvent::Knockback { - entity: target_entity, - impulse, - }); - } - }, - } - } - } - } - server_events -} diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index 139ccc7cd6..2b3a419d44 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -1,4 +1,3 @@ -use crate::apply_attack; use common::{ comp::{buff, group, Body, CharacterState, Health, Inventory, MeleeAttack, Ori, Pos, Scale}, event::{EventBus, LocalEvent, ServerEvent}, @@ -76,7 +75,15 @@ impl<'a> System<'a> for Sys { attack.applied = true; // Go through all other entities - for (b, pos_b, scale_b_maybe, health_b, body_b, char_state_b_maybe, inventory_b_maybe) in ( + for ( + b, + pos_b, + scale_b_maybe, + health_b, + body_b, + char_state_b_maybe, + inventory_b_maybe, + ) in ( &entities, &positions, scales.maybe(), @@ -122,14 +129,10 @@ impl<'a> System<'a> for Sys { let dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - let server_events = apply_attack( - &attack.attack, - target_group, - b, - inventory_b_maybe, - *uid, - dir, - ); + let server_events = + attack + .attack + .apply_attack(target_group, b, inventory_b_maybe, *uid, dir); for event in server_events { server_emitter.emit(event); diff --git a/common/sys/src/projectile.rs b/common/sys/src/projectile.rs index 79dc67467b..0077ca9c19 100644 --- a/common/sys/src/projectile.rs +++ b/common/sys/src/projectile.rs @@ -118,8 +118,12 @@ impl<'a> System<'a> for Sys { uid_allocator.retrieve_entity_internal(other.into()) { let other_entity_inventory = inventories.get(other_entity); - let change = - damage.modify_damage(other_entity_inventory, projectile.owner); + let change = damage.modify_damage( + other_entity_inventory, + projectile.owner, + false, + 0.0, + ); server_emitter.emit(ServerEvent::Damage { entity: other_entity, change, diff --git a/common/sys/src/shockwave.rs b/common/sys/src/shockwave.rs index 8c7d72e9c3..7602aa7286 100644 --- a/common/sys/src/shockwave.rs +++ b/common/sys/src/shockwave.rs @@ -199,8 +199,9 @@ impl<'a> System<'a> for Sys { } let owner_uid = shockwave.owner.unwrap_or(*uid); - let change = damage.modify_damage(inventories.get(b), Some(owner_uid)); let poise_change = poise_damage.modify_poise_damage(inventories.get(b)); + let change = + damage.modify_damage(inventories.get(b), Some(owner_uid), false, 0.0); server_emitter.emit(ServerEvent::Damage { entity: b, change }); shockwave_hit_list.hit_entities.push(*uid_b); diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 7c8e31ca2d..15d5d4866c 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -509,7 +509,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) source: DamageSource::Falling, value: falldmg, }; - let change = damage.modify_damage(inventories.get(entity), None); + let change = damage.modify_damage(inventories.get(entity), None, false, 0.0); health.change_by(change); } // Handle poise change diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index aed6f77790..da9d254a41 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -90,7 +90,7 @@ impl StateExt for State { }, Effect::Damage(damage) => { let inventories = self.ecs().read_storage::(); - let change = damage.modify_damage(inventories.get(entity), source); + let change = damage.modify_damage(inventories.get(entity), source, false, 0.0); self.ecs() .write_storage::() .get_mut(entity) From 6d509932dba8c34c2e307323e23968e8d7672ab3 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 26 Jan 2021 12:58:52 -0500 Subject: [PATCH 06/23] Attacks can now reward energy. --- common/src/combat.rs | 17 ++++++++++++++--- common/src/states/basic_melee.rs | 20 ++++---------------- common/src/states/combo_melee.rs | 16 +++++++--------- common/sys/src/melee.rs | 16 ++++++++++++---- 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index daa923c8d6..af768f6efc 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -9,7 +9,8 @@ use crate::{ slot::EquipSlot, }, skills::{SkillGroupKind, SkillSet}, - Body, BuffKind, Health, HealthChange, HealthSource, Inventory, Stats, + Body, BuffKind, EnergyChange, EnergySource, Health, HealthChange, HealthSource, Inventory, + Stats, }, effect, event::ServerEvent, @@ -66,6 +67,7 @@ impl Attack { pub fn apply_attack( &self, target_group: GroupTarget, + attacker_entity: EcsEntity, target_entity: EcsEntity, inventory: Option<&Inventory>, uid: Uid, @@ -99,6 +101,15 @@ impl Attack { }); } }, + AttackEffect::EnergyReward(ec) => { + server_events.push(ServerEvent::EnergyChange { + entity: attacker_entity, + change: EnergyChange { + amount: *ec as i32, + source: EnergySource::HitEnemy, + }, + }); + }, } } } @@ -146,8 +157,8 @@ pub enum AttackEffect { //Heal(f32), //Buff(effect::BuffEffect), Knockback(Knockback), - /*EnergyChange(f32), - *Lifesteal(f32), */ + EnergyReward(u32), + //Lifesteal(f32), } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index e5c080bdee..4ba00e92d6 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -1,9 +1,6 @@ use crate::{ combat::{Attack, AttackEffect, DamageComponent}, - comp::{ - CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, - StateUpdate, - }, + comp::{CharacterState, MeleeAttack, PoiseChange, PoiseSource, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -101,8 +98,10 @@ impl CharacterBehavior for Data { strength: self.static_data.knockback, direction: KnockbackDir::Away, }); + let energy = AttackEffect::EnergyReward(50); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback); + .with_effect(knockback) + .with_effect(energy); let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); // Hit attempt @@ -156,17 +155,6 @@ impl CharacterBehavior for Data { }, } - // Grant energy on successful hit - if let Some(attack) = data.melee_attack { - if attack.applied && attack.hit_count > 0 { - data.updater.remove::(data.entity); - update.energy.change_by(EnergyChange { - amount: 50, - source: EnergySource::HitEnemy, - }); - } - } - update } } diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index b7913a8edf..c348a68ec9 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -191,8 +191,14 @@ impl CharacterBehavior for Data { strength: self.static_data.stage_data[stage_index].knockback, direction: KnockbackDir::Away, }); + let energy = self.static_data.max_energy_gain.min( + self.static_data.initial_energy_gain + + self.combo * self.static_data.energy_increase, + ); + let energy = AttackEffect::EnergyReward(energy); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback); + .with_effect(knockback) + .with_effect(energy); let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); data.updater.insert(data.entity, MeleeAttack { @@ -287,10 +293,6 @@ impl CharacterBehavior for Data { // Grant energy on successful hit if let Some(attack) = data.melee_attack { if attack.applied && attack.hit_count > 0 { - let energy = self.static_data.max_energy_gain.min( - self.static_data.initial_energy_gain - + self.combo * self.static_data.energy_increase, - ) as i32; update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), stage: self.stage, @@ -300,10 +302,6 @@ impl CharacterBehavior for Data { next_stage: self.next_stage, }); data.updater.remove::(data.entity); - update.energy.change_by(EnergyChange { - amount: energy, - source: EnergySource::HitEnemy, - }); } } diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index 2b3a419d44..8573561474 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -129,10 +129,18 @@ impl<'a> System<'a> for Sys { let dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - let server_events = - attack - .attack - .apply_attack(target_group, b, inventory_b_maybe, *uid, dir); + let server_events = attack.attack.apply_attack( + target_group, + entity, + b, + inventory_b_maybe, + *uid, + dir, + ); + + if !server_events.is_empty() { + attack.hit_count += 1; + } for event in server_events { server_emitter.emit(event); From 78879d5189f437fe9ff19280922b13858102a69a Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 27 Jan 2021 20:44:49 -0500 Subject: [PATCH 07/23] Attacks can now inflict de/buffs. --- common/src/combat.rs | 77 ++++++++++++++++++++++++++---- common/src/states/basic_melee.rs | 6 ++- common/src/states/charged_melee.rs | 6 ++- common/src/states/combo_melee.rs | 6 ++- common/src/states/dash_melee.rs | 6 ++- common/src/states/leap_melee.rs | 6 ++- common/src/states/spin_melee.rs | 6 ++- common/sys/src/melee.rs | 53 +------------------- 8 files changed, 93 insertions(+), 73 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index af768f6efc..9d24a06394 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -1,5 +1,6 @@ use crate::{ comp::{ + buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource}, inventory::{ item::{ armor::Protection, @@ -9,10 +10,8 @@ use crate::{ slot::EquipSlot, }, skills::{SkillGroupKind, SkillSet}, - Body, BuffKind, EnergyChange, EnergySource, Health, HealthChange, HealthSource, Inventory, - Stats, + Body, EnergyChange, EnergySource, Health, HealthChange, HealthSource, Inventory, Stats, }, - effect, event::ServerEvent, uid::Uid, util::Dir, @@ -20,6 +19,7 @@ use crate::{ use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; use specs::Entity as EcsEntity; +use std::time::Duration; use vek::*; #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -70,7 +70,7 @@ impl Attack { attacker_entity: EcsEntity, target_entity: EcsEntity, inventory: Option<&Inventory>, - uid: Uid, + attacker_uid: Uid, dir: Dir, ) -> Vec { let is_crit = thread_rng().gen::() < self.crit_chance; @@ -81,10 +81,12 @@ impl Attack { .iter() .filter(|d| d.target.map_or(true, |t| t == target_group)) { - let change = - damage - .damage - .modify_damage(inventory, Some(uid), is_crit, self.crit_multiplier); + let change = damage.damage.modify_damage( + inventory, + Some(attacker_uid), + is_crit, + self.crit_multiplier, + ); if change.amount != 0 { server_events.push(ServerEvent::Damage { entity: target_entity, @@ -110,6 +112,16 @@ impl Attack { }, }); }, + AttackEffect::Buff(b) => { + if thread_rng().gen::() < b.chance { + server_events.push(ServerEvent::Buff { + entity: target_entity, + buff_change: BuffChange::Add( + b.to_buff(attacker_uid, -change.amount as f32), + ), + }); + } + }, } } } @@ -155,7 +167,7 @@ impl EffectComponent { #[derive(Debug, Serialize, Deserialize)] pub enum AttackEffect { //Heal(f32), - //Buff(effect::BuffEffect), + Buff(CombatBuff), Knockback(Knockback), EnergyReward(u32), //Lifesteal(f32), @@ -358,6 +370,53 @@ impl Knockback { } } +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct CombatBuff { + pub kind: BuffKind, + pub dur_secs: f32, + pub strength: CombatBuffStrength, + pub chance: f32, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum CombatBuffStrength { + DamageFraction(f32), + Value(f32), +} + +impl CombatBuffStrength { + fn to_strength(self, damage: f32) -> f32 { + match self { + CombatBuffStrength::DamageFraction(f) => damage * f, + CombatBuffStrength::Value(v) => v, + } + } +} + +impl CombatBuff { + fn to_buff(self, uid: Uid, damage: f32) -> Buff { + // TODO: Generate BufCategoryId vec (probably requires damage overhaul?) + Buff::new( + self.kind, + BuffData::new( + self.strength.to_strength(damage), + Some(Duration::from_secs_f32(self.dur_secs)), + ), + Vec::new(), + BuffSource::Character { by: uid }, + ) + } + + pub fn default_melee() -> Self { + Self { + kind: BuffKind::Bleeding, + dur_secs: 10.0, + strength: CombatBuffStrength::DamageFraction(0.1), + chance: 0.1, + } + } +} + fn equipped_tool(inv: &Inventory, slot: EquipSlot) -> Option<&Tool> { inv.equipped(slot).and_then(|i| { if let ItemKind::Tool(tool) = &i.kind() { diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 4ba00e92d6..18d636823a 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -1,5 +1,5 @@ use crate::{ - combat::{Attack, AttackEffect, DamageComponent}, + combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, comp::{CharacterState, MeleeAttack, PoiseChange, PoiseSource, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, @@ -99,9 +99,11 @@ impl CharacterBehavior for Data { direction: KnockbackDir::Away, }); let energy = AttackEffect::EnergyReward(50); + let buff = AttackEffect::Buff(CombatBuff::default_melee()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) - .with_effect(energy); + .with_effect(energy) + .with_effect(buff); let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); // Hit attempt diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 592ecbdc22..45954fad00 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -1,5 +1,5 @@ use crate::{ - combat::{Attack, AttackEffect, DamageComponent}, + combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -173,8 +173,10 @@ impl CharacterBehavior for Data { strength: knockback, direction: KnockbackDir::Away, }); + let buff = AttackEffect::Buff(CombatBuff::default_melee()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback); + .with_effect(knockback) + .with_effect(buff); let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); // Hit attempt diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index c348a68ec9..29265b0fd5 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -1,5 +1,5 @@ use crate::{ - combat::{Attack, AttackEffect, DamageComponent}, + combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -196,9 +196,11 @@ impl CharacterBehavior for Data { + self.combo * self.static_data.energy_increase, ); let energy = AttackEffect::EnergyReward(energy); + let buff = AttackEffect::Buff(CombatBuff::default_melee()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) - .with_effect(energy); + .with_effect(energy) + .with_effect(buff); let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index ca6677663d..8c7c1ae44c 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -1,5 +1,5 @@ use crate::{ - combat::{Attack, AttackEffect, DamageComponent}, + combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -150,9 +150,11 @@ impl CharacterBehavior for Data { strength: knockback, direction: KnockbackDir::Away, }); + let buff = AttackEffect::Buff(CombatBuff::default_melee()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback); + .with_effect(knockback) + .with_effect(buff); let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index fae603386f..ff7ff4f0e3 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -1,5 +1,5 @@ use crate::{ - combat::{Attack, AttackEffect, DamageComponent}, + combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, comp::{CharacterState, MeleeAttack, PoiseChange, PoiseSource, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, @@ -156,8 +156,10 @@ impl CharacterBehavior for Data { strength: self.static_data.knockback, direction: KnockbackDir::Away, }); + let buff = AttackEffect::Buff(CombatBuff::default_melee()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback); + .with_effect(knockback) + .with_effect(buff); let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); // Hit attempt, when animation plays diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 5b172bbf05..456d8fb52c 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -1,5 +1,5 @@ use crate::{ - combat::{Attack, AttackEffect, DamageComponent}, + combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -124,8 +124,10 @@ impl CharacterBehavior for Data { strength: self.static_data.knockback, direction: KnockbackDir::Away, }); + let buff = AttackEffect::Buff(CombatBuff::default_melee()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback); + .with_effect(knockback) + .with_effect(buff); let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); // Hit attempt diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index 8573561474..2c7aee86ce 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -1,5 +1,5 @@ use common::{ - comp::{buff, group, Body, CharacterState, Health, Inventory, MeleeAttack, Ori, Pos, Scale}, + comp::{group, Body, CharacterState, Health, Inventory, MeleeAttack, Ori, Pos, Scale}, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, span, @@ -7,9 +7,7 @@ use common::{ util::Dir, GroupTarget, }; -use rand::{thread_rng, Rng}; use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; -use std::time::Duration; use vek::*; /// This system is responsible for handling accepted inputs like moving or @@ -145,55 +143,6 @@ impl<'a> System<'a> for Sys { for event in server_events { server_emitter.emit(event); } - - /*for (target, damage) in attack.damages.iter() { - if let Some(target) = target { - if *target != target_group - || (!matches!(target, GroupTarget::InGroup) && is_dodge) - { - continue; - } - } - - let change = damage.modify_damage(inventories.get(b), Some(*uid)); - - server_emitter.emit(ServerEvent::Damage { entity: b, change }); - - // Apply bleeding buff on melee hits with 10% chance - // TODO: Don't have buff uniformly applied on all melee attacks - if change.amount < 0 && thread_rng().gen::() < 0.1 { - use buff::*; - server_emitter.emit(ServerEvent::Buff { - entity: b, - buff_change: BuffChange::Add(Buff::new( - BuffKind::Bleeding, - BuffData { - strength: -change.amount as f32 / 10.0, - duration: Some(Duration::from_secs(10)), - }, - vec![BuffCategory::Physical], - BuffSource::Character { by: *uid }, - )), - }); - } - - let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - let impulse = attack.knockback.calculate_impulse(kb_dir); - if !impulse.is_approx_zero() { - server_emitter.emit(ServerEvent::Knockback { entity: b, impulse }); - } - - let poise_change = poise_change.modify_poise_damage(inventories.get(b)); - if poise_change.amount.abs() > 0 { - server_emitter.emit(ServerEvent::PoiseChange { - entity: b, - change: poise_change, - kb_dir: *kb_dir, - }); - } - - attack.hit_count += 1; - }*/ } } } From 18aa315f0da3e6d74a18fe5d19a4e2a1e99730b3 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 27 Jan 2021 20:54:45 -0500 Subject: [PATCH 08/23] Rolling now dodges melee attacks again. --- common/src/combat.rs | 3 +++ common/sys/src/melee.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/common/src/combat.rs b/common/src/combat.rs index 9d24a06394..9c3c9e4c92 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -64,6 +64,7 @@ impl Attack { self } + #[allow(clippy::too_many_arguments)] pub fn apply_attack( &self, target_group: GroupTarget, @@ -72,6 +73,7 @@ impl Attack { inventory: Option<&Inventory>, attacker_uid: Uid, dir: Dir, + target_dodging: bool, ) -> Vec { let is_crit = thread_rng().gen::() < self.crit_chance; let mut accumulated_damage = 0.0; @@ -80,6 +82,7 @@ impl Attack { .damages .iter() .filter(|d| d.target.map_or(true, |t| t == target_group)) + .filter(|d| !(matches!(d.target, Some(GroupTarget::OutOfGroup)) && target_dodging)) { let change = damage.damage.modify_damage( inventory, diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index 2c7aee86ce..675e5f5a92 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -134,6 +134,7 @@ impl<'a> System<'a> for Sys { inventory_b_maybe, *uid, dir, + is_dodge, ); if !server_events.is_empty() { From c77f263cd1a34e1e123f9e1478d8edeedb3b2914 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 27 Jan 2021 21:15:25 -0500 Subject: [PATCH 09/23] Effects now work when not tied to a damage. --- common/src/combat.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 9c3c9e4c92..693f53146f 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -90,6 +90,8 @@ impl Attack { is_crit, self.crit_multiplier, ); + let damage_damage = -change.amount as f32; + accumulated_damage += damage_damage; if change.amount != 0 { server_events.push(ServerEvent::Damage { entity: target_entity, @@ -120,7 +122,7 @@ impl Attack { server_events.push(ServerEvent::Buff { entity: target_entity, buff_change: BuffChange::Add( - b.to_buff(attacker_uid, -change.amount as f32), + b.to_buff(attacker_uid, damage_damage), ), }); } @@ -129,6 +131,43 @@ impl Attack { } } } + for effect in self + .effects + .iter() + .filter(|e| e.target.map_or(true, |t| t == target_group)) + .filter(|e| !(matches!(e.target, Some(GroupTarget::OutOfGroup)) && target_dodging)) + { + match effect.effect { + AttackEffect::Knockback(kb) => { + let impulse = kb.calculate_impulse(dir); + if !impulse.is_approx_zero() { + server_events.push(ServerEvent::Knockback { + entity: target_entity, + impulse, + }); + } + }, + AttackEffect::EnergyReward(ec) => { + server_events.push(ServerEvent::EnergyChange { + entity: attacker_entity, + change: EnergyChange { + amount: ec as i32, + source: EnergySource::HitEnemy, + }, + }); + }, + AttackEffect::Buff(b) => { + if thread_rng().gen::() < b.chance { + server_events.push(ServerEvent::Buff { + entity: target_entity, + buff_change: BuffChange::Add( + b.to_buff(attacker_uid, accumulated_damage), + ), + }); + } + }, + } + } server_events } } From 46dbb8aef4d3629dcb347c6bef0a88adbcf67dff Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 28 Jan 2021 13:50:56 -0500 Subject: [PATCH 10/23] Attack effects can now have requirements. --- common/src/combat.rs | 78 ++++++++++++++++++++------------ common/src/states/basic_melee.rs | 12 +++-- common/src/states/combo_melee.rs | 12 +++-- 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 693f53146f..74c31945ab 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -137,35 +137,40 @@ impl Attack { .filter(|e| e.target.map_or(true, |t| t == target_group)) .filter(|e| !(matches!(e.target, Some(GroupTarget::OutOfGroup)) && target_dodging)) { - match effect.effect { - AttackEffect::Knockback(kb) => { - let impulse = kb.calculate_impulse(dir); - if !impulse.is_approx_zero() { - server_events.push(ServerEvent::Knockback { - entity: target_entity, - impulse, + if match &effect.requirement { + Some(CombatRequirement::AnyDamage) => accumulated_damage != 0.0, + None => true, + } { + match effect.effect { + AttackEffect::Knockback(kb) => { + let impulse = kb.calculate_impulse(dir); + if !impulse.is_approx_zero() { + server_events.push(ServerEvent::Knockback { + entity: target_entity, + impulse, + }); + } + }, + AttackEffect::EnergyReward(ec) => { + server_events.push(ServerEvent::EnergyChange { + entity: attacker_entity, + change: EnergyChange { + amount: ec as i32, + source: EnergySource::HitEnemy, + }, }); - } - }, - AttackEffect::EnergyReward(ec) => { - server_events.push(ServerEvent::EnergyChange { - entity: attacker_entity, - change: EnergyChange { - amount: ec as i32, - source: EnergySource::HitEnemy, - }, - }); - }, - AttackEffect::Buff(b) => { - if thread_rng().gen::() < b.chance { - server_events.push(ServerEvent::Buff { - entity: target_entity, - buff_change: BuffChange::Add( - b.to_buff(attacker_uid, accumulated_damage), - ), - }); - } - }, + }, + AttackEffect::Buff(b) => { + if thread_rng().gen::() < b.chance { + server_events.push(ServerEvent::Buff { + entity: target_entity, + buff_change: BuffChange::Add( + b.to_buff(attacker_uid, accumulated_damage), + ), + }); + } + }, + } } } server_events @@ -198,11 +203,21 @@ impl DamageComponent { pub struct EffectComponent { target: Option, effect: AttackEffect, + requirement: Option, } impl EffectComponent { pub fn new(target: Option, effect: AttackEffect) -> Self { - Self { target, effect } + Self { + target, + effect, + requirement: None, + } + } + + pub fn with_requirement(mut self, requirement: CombatRequirement) -> Self { + self.requirement = Some(requirement); + self } } @@ -215,6 +230,11 @@ pub enum AttackEffect { //Lifesteal(f32), } +#[derive(Debug, Serialize, Deserialize)] +pub enum CombatRequirement { + AnyDamage, +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum DamageSource { Buff(BuffKind), diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 18d636823a..552f90fd16 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -1,5 +1,7 @@ use crate::{ - combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, + combat::{ + Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, + }, comp::{CharacterState, MeleeAttack, PoiseChange, PoiseSource, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, @@ -99,12 +101,16 @@ impl CharacterBehavior for Data { direction: KnockbackDir::Away, }); let energy = AttackEffect::EnergyReward(50); + let energy = EffectComponent::new(None, energy) + .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_melee()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) - .with_effect(energy) .with_effect(buff); - let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); + let attack = Attack::default() + .with_damage(damage) + .with_crit(0.5, 1.3) + .with_effect(energy); // Hit attempt data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 29265b0fd5..0027ee9621 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -1,5 +1,7 @@ use crate::{ - combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, + combat::{ + Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, + }, comp::{ CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, StateUpdate, @@ -196,12 +198,16 @@ impl CharacterBehavior for Data { + self.combo * self.static_data.energy_increase, ); let energy = AttackEffect::EnergyReward(energy); + let energy = EffectComponent::new(None, energy) + .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_melee()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) - .with_effect(energy) .with_effect(buff); - let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); + let attack = Attack::default() + .with_damage(damage) + .with_crit(0.5, 1.3) + .with_effect(energy); data.updater.insert(data.entity, MeleeAttack { attack, From 59ce8c68433c3840897acf6da901c6a128bbe1c7 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 28 Jan 2021 19:04:44 -0500 Subject: [PATCH 11/23] Projectiles now use attacks. --- common/src/combat.rs | 2 +- common/src/comp/projectile.rs | 101 ++++++++++---------- common/src/states/basic_melee.rs | 2 +- common/src/states/charged_melee.rs | 2 +- common/src/states/charged_ranged.rs | 34 +++---- common/src/states/combo_melee.rs | 2 +- common/src/states/dash_melee.rs | 2 +- common/src/states/leap_melee.rs | 2 +- common/src/states/spin_melee.rs | 2 +- common/sys/src/projectile.rs | 140 +++++++++++++--------------- 10 files changed, 140 insertions(+), 149 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 74c31945ab..fc593c5d38 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -469,7 +469,7 @@ impl CombatBuff { ) } - pub fn default_melee() -> Self { + pub fn default_physical() -> Self { Self { kind: BuffKind::Bleeding, dur_secs: 10.0, diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index a49320ffb8..d57978851f 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -1,30 +1,33 @@ use crate::{ - comp::buff::{BuffCategory, BuffData, BuffKind}, - effect::{self, BuffEffect}, + combat::{ + Attack, AttackEffect, CombatBuff, CombatRequirement, Damage, DamageComponent, DamageSource, + EffectComponent, GroupTarget, Knockback, KnockbackDir, + }, + effect, uid::Uid, - Damage, DamageSource, Explosion, GroupTarget, Knockback, KnockbackDir, RadiusEffect, + Explosion, RadiusEffect, }; use serde::{Deserialize, Serialize}; use specs::Component; use specs_idvs::IdvStorage; use std::time::Duration; -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub enum Effect { - Damage(Option, Damage), - Knockback(Knockback), - RewardEnergy(u32), + Attack(Attack), + //Knockback(Knockback), + //RewardEnergy(u32), Explode(Explosion), Vanish, Stick, Possess, - Buff { - buff: BuffEffect, - chance: Option, - }, + /*Buff { + * buff: BuffEffect, + * chance: Option, */ + /* */ } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct Projectile { // TODO: use SmallVec for these effects pub hit_solid: Vec, @@ -74,32 +77,29 @@ impl ProjectileConstructor { knockback, energy_regen, } => { - let buff = BuffEffect { - kind: BuffKind::Bleeding, - data: BuffData { - strength: damage / 2.0, - duration: Some(Duration::from_secs(5)), - }, - cat_ids: vec![BuffCategory::Physical], + let damage = Damage { + source: DamageSource::Projectile, + value: damage, }; + let knockback = AttackEffect::Knockback(Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }); + let energy = AttackEffect::EnergyReward(energy_regen); + let energy = EffectComponent::new(None, energy) + .with_requirement(CombatRequirement::AnyDamage); + let buff = AttackEffect::Buff(CombatBuff::default_physical()); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(knockback) + .with_effect(buff); + let attack = Attack::default() + .with_damage(damage) + .with_crit(0.5, 1.2) + .with_effect(energy); + Projectile { hit_solid: vec![Effect::Stick], - hit_entity: vec![ - Effect::Damage(Some(GroupTarget::OutOfGroup), Damage { - source: DamageSource::Projectile, - value: damage, - }), - Effect::Knockback(Knockback { - strength: knockback, - direction: KnockbackDir::Away, - }), - Effect::RewardEnergy(energy_regen), - Effect::Vanish, - Effect::Buff { - buff, - chance: Some(0.10), - }, - ], + hit_entity: vec![Effect::Attack(attack), Effect::Vanish], time_left: Duration::from_secs(15), owner, ignore_group: true, @@ -148,19 +148,24 @@ impl ProjectileConstructor { Firebolt { damage, energy_regen, - } => Projectile { - hit_solid: vec![Effect::Vanish], - hit_entity: vec![ - Effect::Damage(Some(GroupTarget::OutOfGroup), Damage { - source: DamageSource::Energy, - value: damage, - }), - Effect::RewardEnergy(energy_regen), - Effect::Vanish, - ], - time_left: Duration::from_secs(10), - owner, - ignore_group: true, + } => { + let damage = Damage { + source: DamageSource::Energy, + value: damage, + }; + let energy = AttackEffect::EnergyReward(energy_regen); + let energy = EffectComponent::new(None, energy) + .with_requirement(CombatRequirement::AnyDamage); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); + let attack = Attack::default().with_damage(damage).with_effect(energy); + + Projectile { + hit_solid: vec![Effect::Vanish], + hit_entity: vec![Effect::Attack(attack), Effect::Vanish], + time_left: Duration::from_secs(10), + owner, + ignore_group: true, + } }, Heal { heal, diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 552f90fd16..a7aa3d8b84 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -103,7 +103,7 @@ impl CharacterBehavior for Data { let energy = AttackEffect::EnergyReward(50); let energy = EffectComponent::new(None, energy) .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_melee()); + let buff = AttackEffect::Buff(CombatBuff::default_physical()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) .with_effect(buff); diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 45954fad00..2a17c0bc9b 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -173,7 +173,7 @@ impl CharacterBehavior for Data { strength: knockback, direction: KnockbackDir::Away, }); - let buff = AttackEffect::Buff(CombatBuff::default_melee()); + let buff = AttackEffect::Buff(CombatBuff::default_physical()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) .with_effect(buff); diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index db86dad67d..f53f7b2f14 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -1,16 +1,17 @@ use crate::{ + combat::{ + Attack, AttackEffect, CombatBuff, Damage, DamageComponent, DamageSource, GroupTarget, + Knockback, KnockbackDir, + }, comp::{ - buff::{BuffCategory, BuffData, BuffKind}, projectile, Body, CharacterState, EnergyChange, EnergySource, Gravity, LightEmitter, Projectile, StateUpdate, }, - effect::BuffEffect, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, - Damage, DamageSource, GroupTarget, Knockback, KnockbackDir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -109,27 +110,22 @@ impl CharacterBehavior for Data { }; let knockback = self.static_data.initial_knockback + charge_frac * self.static_data.scaled_knockback; + let knockback = AttackEffect::Knockback(Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }); + let buff = AttackEffect::Buff(CombatBuff::default_physical()); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(knockback) + .with_effect(buff); + let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.2); + // Fire let projectile = Projectile { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ - projectile::Effect::Damage(Some(GroupTarget::OutOfGroup), damage), - projectile::Effect::Knockback(Knockback { - strength: knockback, - direction: KnockbackDir::Away, - }), + projectile::Effect::Attack(attack), projectile::Effect::Vanish, - projectile::Effect::Buff { - buff: BuffEffect { - kind: BuffKind::Bleeding, - data: BuffData { - strength: damage.value / 5.0, - duration: Some(Duration::from_secs(5)), - }, - cat_ids: vec![BuffCategory::Physical], - }, - chance: Some(0.10), - }, ], time_left: Duration::from_secs(15), owner: Some(*data.uid), diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 0027ee9621..c8fd627d8f 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -200,7 +200,7 @@ impl CharacterBehavior for Data { let energy = AttackEffect::EnergyReward(energy); let energy = EffectComponent::new(None, energy) .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_melee()); + let buff = AttackEffect::Buff(CombatBuff::default_physical()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) .with_effect(buff); diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 8c7c1ae44c..53a023cd02 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -150,7 +150,7 @@ impl CharacterBehavior for Data { strength: knockback, direction: KnockbackDir::Away, }); - let buff = AttackEffect::Buff(CombatBuff::default_melee()); + let buff = AttackEffect::Buff(CombatBuff::default_physical()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index ff7ff4f0e3..b6463d2e86 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -156,7 +156,7 @@ impl CharacterBehavior for Data { strength: self.static_data.knockback, direction: KnockbackDir::Away, }); - let buff = AttackEffect::Buff(CombatBuff::default_melee()); + let buff = AttackEffect::Buff(CombatBuff::default_physical()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) .with_effect(buff); diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 456d8fb52c..36b3a26c7d 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -124,7 +124,7 @@ impl CharacterBehavior for Data { strength: self.static_data.knockback, direction: KnockbackDir::Away, }); - let buff = AttackEffect::Buff(CombatBuff::default_melee()); + let buff = AttackEffect::Buff(CombatBuff::default_physical()); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) .with_effect(buff); diff --git a/common/sys/src/projectile.rs b/common/sys/src/projectile.rs index 0077ca9c19..bae042cecd 100644 --- a/common/sys/src/projectile.rs +++ b/common/sys/src/projectile.rs @@ -1,9 +1,5 @@ use common::{ - comp::{ - buff::{Buff, BuffChange, BuffSource}, - projectile, EnergyChange, EnergySource, Group, HealthSource, Inventory, Ori, PhysicsState, - Pos, Projectile, Vel, - }, + comp::{projectile, Group, HealthSource, Inventory, Ori, PhysicsState, Pos, Projectile, Vel}, event::{EventBus, ServerEvent}, metrics::SysMetrics, resources::DeltaTime, @@ -11,7 +7,6 @@ use common::{ uid::UidAllocator, GroupTarget, }; -use rand::{thread_rng, Rng}; use specs::{ saveload::MarkerAllocator, Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage, }; @@ -103,60 +98,55 @@ impl<'a> System<'a> for Sys { let projectile = &mut *projectile; for effect in projectile.hit_entity.drain(..) { match effect { - projectile::Effect::Damage(target, damage) => { - if Some(other) == projectile.owner { - continue; - } + projectile::Effect::Attack(attack) => { + if let Some(owner) = projectile.owner { + if let (Some(owner_entity), Some(target_entity)) = ( + uid_allocator.retrieve_entity_internal(owner.into()), + uid_allocator.retrieve_entity_internal(other.into()), + ) { + let server_events = attack.apply_attack( + target_group, + owner_entity, + target_entity, + inventories.get(target_entity), + owner, + ori.0, + false, + ); - if let Some(target) = target { - if target != target_group { - continue; - } - } - - if let Some(other_entity) = - uid_allocator.retrieve_entity_internal(other.into()) - { - let other_entity_inventory = inventories.get(other_entity); - let change = damage.modify_damage( - other_entity_inventory, - projectile.owner, - false, - 0.0, - ); - server_emitter.emit(ServerEvent::Damage { - entity: other_entity, - change, - }); - } - }, - projectile::Effect::Knockback(knockback) => { - if let Some(other_entity) = - uid_allocator.retrieve_entity_internal(other.into()) - { - let impulse = knockback.calculate_impulse(ori.0); - if !impulse.is_approx_zero() { - server_emitter.emit(ServerEvent::Knockback { - entity: other_entity, - impulse, - }); + for event in server_events { + server_emitter.emit(event); + } } } }, - projectile::Effect::RewardEnergy(energy) => { - if let Some(entity_owner) = projectile - .owner - .and_then(|u| uid_allocator.retrieve_entity_internal(u.into())) - { - server_emitter.emit(ServerEvent::EnergyChange { - entity: entity_owner, - change: EnergyChange { - amount: energy as i32, - source: EnergySource::HitEnemy, - }, - }); - } - }, + // projectile::Effect::Knockback(knockback) => { + // if let Some(other_entity) = + // uid_allocator.retrieve_entity_internal(other.into()) + // { + // let impulse = knockback.calculate_impulse(ori.0); + // if !impulse.is_approx_zero() { + // server_emitter.emit(ServerEvent::Knockback { + // entity: other_entity, + // impulse, + // }); + // } + // } + // }, + // projectile::Effect::RewardEnergy(energy) => { + // if let Some(entity_owner) = projectile + // .owner + // .and_then(|u| uid_allocator.retrieve_entity_internal(u.into())) + // { + // server_emitter.emit(ServerEvent::EnergyChange { + // entity: entity_owner, + // change: EnergyChange { + // amount: energy as i32, + // source: EnergySource::HitEnemy, + // }, + // }); + // } + // }, projectile::Effect::Explode(e) => { server_emitter.emit(ServerEvent::Explosion { pos: pos.0, @@ -180,25 +170,25 @@ impl<'a> System<'a> for Sys { } }, // TODO: Change to effect after !1472 merges - projectile::Effect::Buff { buff, chance } => { - if let Some(entity) = - uid_allocator.retrieve_entity_internal(other.into()) - { - if chance.map_or(true, |c| thread_rng().gen::() < c) { - let source = if let Some(owner) = projectile.owner { - BuffSource::Character { by: owner } - } else { - BuffSource::Unknown - }; - let buff = - Buff::new(buff.kind, buff.data, buff.cat_ids, source); - server_emitter.emit(ServerEvent::Buff { - entity, - buff_change: BuffChange::Add(buff), - }); - } - } - }, + // projectile::Effect::Buff { buff, chance } => { + // if let Some(entity) = + // uid_allocator.retrieve_entity_internal(other.into()) + // { + // if chance.map_or(true, |c| thread_rng().gen::() < c) { + // let source = if let Some(owner) = projectile.owner { + // BuffSource::Character { by: owner } + // } else { + // BuffSource::Unknown + // }; + // let buff = + // Buff::new(buff.kind, buff.data, buff.cat_ids, source); + // server_emitter.emit(ServerEvent::Buff { + // entity, + // buff_change: BuffChange::Add(buff), + // }); + // } + // } + // }, _ => {}, } } From fdef168e8236a768a04610cdc3448ac04ac25f82 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 28 Jan 2021 20:37:33 -0500 Subject: [PATCH 12/23] Beams now use Attack, with limited functionality in some cases. --- common/src/combat.rs | 12 +-- common/src/comp/beam.rs | 15 +-- common/src/comp/projectile.rs | 8 +- common/src/comp/shockwave.rs | 4 +- common/src/states/basic_beam.rs | 20 ++-- common/sys/src/beam.rs | 156 +++++++++++++++++---------- server/src/events/entity_creation.rs | 2 +- 7 files changed, 130 insertions(+), 87 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index fc593c5d38..cf13842281 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -28,7 +28,7 @@ pub enum GroupTarget { OutOfGroup, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] // TODO: Yeet clone derive pub struct Attack { damages: Vec, effects: Vec, @@ -177,7 +177,7 @@ impl Attack { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct DamageComponent { damage: Damage, target: Option, @@ -199,7 +199,7 @@ impl DamageComponent { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct EffectComponent { target: Option, effect: AttackEffect, @@ -221,7 +221,7 @@ impl EffectComponent { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum AttackEffect { //Heal(f32), Buff(CombatBuff), @@ -230,12 +230,12 @@ pub enum AttackEffect { //Lifesteal(f32), } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum CombatRequirement { AnyDamage, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum DamageSource { Buff(BuffKind), Melee, diff --git a/common/src/comp/beam.rs b/common/src/comp/beam.rs index 7aafcedffd..f5d06266e2 100644 --- a/common/src/comp/beam.rs +++ b/common/src/comp/beam.rs @@ -1,22 +1,23 @@ -use crate::{uid::Uid, Damage, GroupTarget}; +use crate::{combat::Attack, uid::Uid}; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; use specs_idvs::IdvStorage; use std::time::Duration; -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Properties { + pub attack: Attack, pub angle: f32, pub speed: f32, - pub damages: Vec<(Option, Damage)>, - pub lifesteal_eff: f32, - pub energy_regen: u32, - pub energy_cost: u32, + //pub lifesteal_eff: f32, + //pub energy_regen: u32, + //pub energy_cost: u32, pub duration: Duration, pub owner: Option, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +// TODO: Separate components out for cheaper network syncing +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct BeamSegment { pub properties: Properties, #[serde(skip)] diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index d57978851f..06bb97f7e3 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -21,10 +21,10 @@ pub enum Effect { Vanish, Stick, Possess, - /*Buff { - * buff: BuffEffect, - * chance: Option, */ - /* */ + /* Buff { + * buff: BuffEffect, + * chance: Option, + * }, */ } #[derive(Debug, Serialize, Deserialize)] diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs index a844bc29c3..cae93eb73c 100644 --- a/common/src/comp/shockwave.rs +++ b/common/src/comp/shockwave.rs @@ -4,7 +4,7 @@ use specs::{Component, DerefFlaggedStorage}; use specs_idvs::IdvStorage; use std::time::Duration; -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Properties { pub angle: f32, pub vertical_angle: f32, @@ -16,7 +16,7 @@ pub struct Properties { pub owner: Option, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Shockwave { pub properties: Properties, #[serde(skip)] diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 07bac71c76..5f23c595df 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -1,4 +1,8 @@ use crate::{ + combat::{ + Attack, AttackEffect, CombatRequirement, Damage, DamageComponent, DamageSource, + EffectComponent, GroupTarget, + }, comp::{beam, Body, CharacterState, EnergyChange, EnergySource, Ori, Pos, StateUpdate}, event::ServerEvent, states::{ @@ -6,7 +10,6 @@ use crate::{ utils::*, }, uid::Uid, - Damage, DamageSource, GroupTarget, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -129,16 +132,17 @@ impl CharacterBehavior for Data { }; let speed = self.static_data.range / self.static_data.beam_duration.as_secs_f32(); + + let energy = AttackEffect::EnergyReward(self.static_data.energy_regen); + let energy = EffectComponent::new(None, energy) + .with_requirement(CombatRequirement::AnyDamage); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); + let attack = Attack::default().with_damage(damage).with_effect(energy); + let properties = beam::Properties { + attack, angle: self.static_data.max_angle.to_radians(), speed, - damages: vec![ - (Some(GroupTarget::OutOfGroup), damage), - (Some(GroupTarget::InGroup), heal), - ], - lifesteal_eff: self.static_data.lifesteal_eff, - energy_regen: self.static_data.energy_regen, - energy_cost: self.static_data.energy_cost, duration: self.static_data.beam_duration, owner: Some(*data.uid), }; diff --git a/common/sys/src/beam.rs b/common/sys/src/beam.rs index 1dc2041117..6fa408b253 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -1,7 +1,7 @@ use common::{ comp::{ - group, Beam, BeamSegment, Body, Energy, EnergyChange, EnergySource, Health, HealthChange, - HealthSource, Inventory, Last, Ori, Pos, Scale, + group, Beam, BeamSegment, Body, Energy, Health, HealthSource, Inventory, Last, Ori, Pos, + Scale, }, event::{EventBus, ServerEvent}, resources::{DeltaTime, Time}, @@ -112,7 +112,16 @@ impl<'a> System<'a> for Sys { }; // Go through all other effectable entities - for (b, uid_b, pos_b, last_pos_b_maybe, scale_b_maybe, health_b, body_b) in ( + for ( + b, + uid_b, + pos_b, + last_pos_b_maybe, + scale_b_maybe, + health_b, + body_b, + inventory_b_maybe, + ) in ( &entities, &uids, &positions, @@ -121,6 +130,7 @@ impl<'a> System<'a> for Sys { scales.maybe(), &healths, &bodies, + inventories.maybe(), ) .join() { @@ -158,68 +168,96 @@ impl<'a> System<'a> for Sys { continue; } - for (target, damage) in beam_segment.damages.iter() { - if let Some(target) = target { - if *target != target_group { - continue; - } - } - - // Modify damage - let change = damage.modify_damage( - inventories.get(b), - beam_segment.owner, + if let (Some(beam_owner), Some(owner_uid)) = (beam_owner, beam_segment.owner) { + let server_events = beam_segment.properties.attack.apply_attack( + target_group, + beam_owner, + b, + inventory_b_maybe, + owner_uid, + ori.0, false, - 0.0, ); - match target { - Some(GroupTarget::OutOfGroup) => { - server_emitter.emit(ServerEvent::Damage { entity: b, change }); - if let Some(entity) = beam_owner { - server_emitter.emit(ServerEvent::Damage { - entity, - change: HealthChange { - amount: (-change.amount as f32 - * beam_segment.lifesteal_eff) - as i32, - cause: HealthSource::Heal { - by: beam_segment.owner, - }, - }, - }); - server_emitter.emit(ServerEvent::EnergyChange { - entity, - change: EnergyChange { - amount: beam_segment.energy_regen as i32, - source: EnergySource::HitEnemy, - }, - }); - } - }, - Some(GroupTarget::InGroup) => { - if let Some(energy) = beam_owner.and_then(|o| energies.get(o)) { - if energy.current() > beam_segment.energy_cost { - server_emitter.emit(ServerEvent::EnergyChange { - entity: beam_owner.unwrap(), /* If it's able to get an energy - * component, the entity exists */ - change: EnergyChange { - amount: -(beam_segment.energy_cost as i32), // Stamina use - source: EnergySource::Ability, - }, - }); - server_emitter - .emit(ServerEvent::Damage { entity: b, change }); - } - } - }, - None => {}, + if !server_events.is_empty() { + hit_entities.push(*uid_b); } - // Adds entities that were hit to the hit_entities list on the beam, sees if - // it needs to purge the hit_entities list - hit_entities.push(*uid_b); + for event in server_events { + server_emitter.emit(event); + } } + + // for (target, damage) in beam_segment.damages.iter() { + // if let Some(target) = target { + // if *target != target_group { + // continue; + // } + // } + + // // Modify damage + // let change = damage.modify_damage( + // inventories.get(b), + // beam_segment.owner, + // false, + // 0.0, + // ); + + // match target { + // Some(GroupTarget::OutOfGroup) => { + // server_emitter.emit(ServerEvent::Damage { + // entity: b, change }); if + // let Some(entity) = beam_owner { + // server_emitter.emit(ServerEvent::Damage { + // entity, + // change: HealthChange { + // amount: (-change.amount as f32 + // * beam_segment.lifesteal_eff) + // as i32, + // cause: HealthSource::Heal { + // by: beam_segment.owner, + // }, + // }, + // }); + // + // server_emitter.emit(ServerEvent::EnergyChange { + // entity, + // change: EnergyChange { + // amount: beam_segment.energy_regen + // as i32, + // source: EnergySource::HitEnemy, + // }, + // }); + // } + // }, + // Some(GroupTarget::InGroup) => { + // if let Some(energy) = beam_owner.and_then(|o| + // energies.get(o)) { if + // energy.current() > beam_segment.energy_cost { + // + // server_emitter.emit(ServerEvent::EnergyChange { + // entity: beam_owner.unwrap(), /* + // If it's able to get an energy + // * component, the entity exists */ + // change: EnergyChange { + // amount: + // -(beam_segment.energy_cost as i32), // Stamina use + // source: + // EnergySource::Ability, + // }, }); + // server_emitter + // .emit(ServerEvent::Damage { + // entity: b, change }); + // } } + // }, + // None => {}, + // } + + // // Adds entities that were hit to the hit_entities + // list on the beam, sees if // it needs + // to purge the hit_entities list + // hit_entities.push(*uid_b); + // } } } } diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 03fa9dd55a..ca4930eb95 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -170,7 +170,7 @@ pub fn handle_beam(server: &mut Server, properties: beam::Properties, pos: Pos, let ecs = state.ecs(); ecs.write_resource::>().push(Outcome::Beam { pos: pos.0, - heal: properties.lifesteal_eff > 0.0, + heal: false, //properties.lifesteal_eff > 0.0, }); state.create_beam(properties, pos, ori).build(); } From fcd89a5d41dffd950941e9177c13cad2abfaaaa4 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 28 Jan 2021 20:55:43 -0500 Subject: [PATCH 13/23] Lifesteal now functional on Attack. --- common/src/combat.rs | 26 +++++++++++++++++++++++++- common/src/states/basic_beam.rs | 4 +++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index cf13842281..8c8bfdca3a 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -127,6 +127,18 @@ impl Attack { }); } }, + AttackEffect::Lifesteal(l) => { + let change = HealthChange { + amount: (damage_damage * l) as i32, + cause: HealthSource::Heal { + by: Some(attacker_uid), + }, + }; + server_events.push(ServerEvent::Damage { + entity: attacker_entity, + change, + }); + }, } } } @@ -170,6 +182,18 @@ impl Attack { }); } }, + AttackEffect::Lifesteal(l) => { + let change = HealthChange { + amount: (accumulated_damage * l) as i32, + cause: HealthSource::Heal { + by: Some(attacker_uid), + }, + }; + server_events.push(ServerEvent::Damage { + entity: attacker_entity, + change, + }); + }, } } } @@ -227,7 +251,7 @@ pub enum AttackEffect { Buff(CombatBuff), Knockback(Knockback), EnergyReward(u32), - //Lifesteal(f32), + Lifesteal(f32), } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 5f23c595df..5c367e38d1 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -136,7 +136,9 @@ impl CharacterBehavior for Data { let energy = AttackEffect::EnergyReward(self.static_data.energy_regen); let energy = EffectComponent::new(None, energy) .with_requirement(CombatRequirement::AnyDamage); - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); + let lifesteal = AttackEffect::Lifesteal(self.static_data.lifesteal_eff); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(lifesteal); let attack = Attack::default().with_damage(damage).with_effect(energy); let properties = beam::Properties { From edcfcc31f69db38054ed777ae5c6885c73fe2ee4 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 29 Jan 2021 23:40:03 -0500 Subject: [PATCH 14/23] Attacks can now deal poise damage. --- common/src/combat.rs | 18 ++++++++++++++++++ common/src/comp/character_state.rs | 2 +- common/src/comp/poise.rs | 11 +++++++++++ common/src/states/basic_melee.rs | 8 ++++++-- common/src/states/charged_melee.rs | 23 ++++++++++++----------- common/src/states/combo_melee.rs | 13 +++++++------ common/src/states/dash_melee.rs | 23 ++++++++++++----------- common/src/states/leap_melee.rs | 14 +++++++++++--- common/src/states/spin_melee.rs | 15 ++++++++++----- 9 files changed, 88 insertions(+), 39 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 8c8bfdca3a..d2d59948e0 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -9,6 +9,7 @@ use crate::{ }, slot::EquipSlot, }, + poise::PoiseChange, skills::{SkillGroupKind, SkillSet}, Body, EnergyChange, EnergySource, Health, HealthChange, HealthSource, Inventory, Stats, }, @@ -139,6 +140,14 @@ impl Attack { change, }); }, + AttackEffect::Poise(p) => { + let change = PoiseChange::from_attack(*p, inventory); + server_events.push(ServerEvent::PoiseChange { + entity: target_entity, + change, + kb_dir: *dir, + }); + }, } } } @@ -194,6 +203,14 @@ impl Attack { change, }); }, + AttackEffect::Poise(p) => { + let change = PoiseChange::from_attack(p, inventory); + server_events.push(ServerEvent::PoiseChange { + entity: target_entity, + change, + kb_dir: *dir, + }); + }, } } } @@ -252,6 +269,7 @@ pub enum AttackEffect { Knockback(Knockback), EnergyReward(u32), Lifesteal(f32), + Poise(f32), } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 5c6711edf4..d0fb1541d1 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -1,6 +1,6 @@ use crate::{ combat::Attack, - comp::{Energy, Ori, PoiseChange, Pos, Vel}, + comp::{Energy, Ori, Pos, Vel}, event::{LocalEvent, ServerEvent}, states::{behavior::JoinData, *}, }; diff --git a/common/src/comp/poise.rs b/common/src/comp/poise.rs index 73864e0ace..ab2ffb46f4 100644 --- a/common/src/comp/poise.rs +++ b/common/src/comp/poise.rs @@ -31,6 +31,17 @@ impl PoiseChange { source: self.source, } } + + /// Creates a poise change from an attack + pub fn from_attack(poise_damage: f32, inventory: Option<&Inventory>) -> Self { + let poise_damage_reduction = + inventory.map_or(0.0, |inv| Poise::compute_poise_damage_reduction(inv)); + let poise_change = -poise_damage * (1.0 - poise_damage_reduction); + Self { + amount: poise_change as i32, + source: PoiseSource::Attack, + } + } } /// Sources of poise change diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index a7aa3d8b84..5b1bd9b9c9 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -2,7 +2,7 @@ use crate::{ combat::{ Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, }, - comp::{CharacterState, MeleeAttack, PoiseChange, PoiseSource, StateUpdate}, + comp::{CharacterState, MeleeAttack, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -96,6 +96,9 @@ impl CharacterBehavior for Data { source: DamageSource::Melee, value: self.static_data.base_damage as f32, }; + let poise = AttackEffect::Poise(self.static_data.base_poise_damage as f32); + let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) + .with_requirement(CombatRequirement::AnyDamage); let knockback = AttackEffect::Knockback(Knockback { strength: self.static_data.knockback, direction: KnockbackDir::Away, @@ -110,7 +113,8 @@ impl CharacterBehavior for Data { let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) - .with_effect(energy); + .with_effect(energy) + .with_effect(poise); // Hit attempt data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 2a17c0bc9b..2558d133cb 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -1,9 +1,8 @@ use crate::{ - combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, - comp::{ - CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, - StateUpdate, + combat::{ + Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, }, + comp::{CharacterState, EnergyChange, EnergySource, MeleeAttack, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, @@ -161,12 +160,11 @@ impl CharacterBehavior for Data { value: self.static_data.initial_damage as f32 + self.charge_amount * self.static_data.scaled_damage as f32, }; - let poise_damage = PoiseChange { - amount: -(self.static_data.initial_poise_damage as f32 - + self.charge_amount * self.static_data.scaled_poise_damage as f32) - as i32, - source: PoiseSource::Attack, - }; + let poise = self.static_data.initial_poise_damage as f32 + + self.charge_amount * self.static_data.scaled_poise_damage as f32; + let poise = AttackEffect::Poise(poise); + let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) + .with_requirement(CombatRequirement::AnyDamage); let knockback = self.static_data.initial_knockback + self.charge_amount * self.static_data.scaled_knockback; let knockback = AttackEffect::Knockback(Knockback { @@ -177,7 +175,10 @@ impl CharacterBehavior for Data { let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) .with_effect(buff); - let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); + let attack = Attack::default() + .with_damage(damage) + .with_crit(0.5, 1.3) + .with_effect(poise); // Hit attempt data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index c8fd627d8f..85763ff24b 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -2,10 +2,7 @@ use crate::{ combat::{ Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, }, - comp::{ - CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, - StateUpdate, - }, + comp::{CharacterState, MeleeAttack, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -178,12 +175,15 @@ impl CharacterBehavior for Data { .min(self.combo / self.static_data.num_stages) * self.static_data.stage_data[stage_index].damage_increase; - let poise_damage = self.static_data.stage_data[stage_index].base_poise_damage + let poise = self.static_data.stage_data[stage_index].base_poise_damage + self .static_data .scales_from_combo .min(self.combo / self.static_data.num_stages) * self.static_data.stage_data[stage_index].poise_damage_increase; + let poise = AttackEffect::Poise(poise as f32); + let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) + .with_requirement(CombatRequirement::AnyDamage); let damage = Damage { source: DamageSource::Melee, @@ -207,7 +207,8 @@ impl CharacterBehavior for Data { let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) - .with_effect(energy); + .with_effect(energy) + .with_effect(poise); data.updater.insert(data.entity, MeleeAttack { attack, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 53a023cd02..93e73ec006 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -1,9 +1,8 @@ use crate::{ - combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, - comp::{ - CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, - StateUpdate, + combat::{ + Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, }, + comp::{CharacterState, EnergyChange, EnergySource, MeleeAttack, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -138,12 +137,11 @@ impl CharacterBehavior for Data { value: self.static_data.base_damage as f32 + charge_frac * self.static_data.scaled_damage as f32, }; - let poise_damage = PoiseChange { - amount: -(self.static_data.base_poise_damage as f32 - + charge_frac * self.static_data.scaled_poise_damage as f32) - as i32, - source: PoiseSource::Attack, - }; + let poise = self.static_data.base_poise_damage as f32 + + charge_frac * self.static_data.scaled_poise_damage as f32; + let poise = AttackEffect::Poise(poise); + let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) + .with_requirement(CombatRequirement::AnyDamage); let knockback = self.static_data.base_knockback + charge_frac * self.static_data.scaled_knockback; let knockback = AttackEffect::Knockback(Knockback { @@ -155,7 +153,10 @@ impl CharacterBehavior for Data { DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) .with_effect(buff); - let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); + let attack = Attack::default() + .with_damage(damage) + .with_crit(0.5, 1.3) + .with_effect(poise); data.updater.insert(data.entity, MeleeAttack { attack, diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index b6463d2e86..d6bb8a2e9b 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -1,6 +1,8 @@ use crate::{ - combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, - comp::{CharacterState, MeleeAttack, PoiseChange, PoiseSource, StateUpdate}, + combat::{ + Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, + }, + comp::{CharacterState, MeleeAttack, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, @@ -152,6 +154,9 @@ impl CharacterBehavior for Data { source: DamageSource::Melee, value: self.static_data.base_damage as f32, }; + let poise = AttackEffect::Poise(self.static_data.base_poise_damage as f32); + let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) + .with_requirement(CombatRequirement::AnyDamage); let knockback = AttackEffect::Knockback(Knockback { strength: self.static_data.knockback, direction: KnockbackDir::Away, @@ -160,7 +165,10 @@ impl CharacterBehavior for Data { let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) .with_effect(buff); - let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); + let attack = Attack::default() + .with_damage(damage) + .with_crit(0.5, 1.3) + .with_effect(poise); // Hit attempt, when animation plays data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 36b3a26c7d..dfd2aa677b 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -1,9 +1,8 @@ use crate::{ - combat::{Attack, AttackEffect, CombatBuff, DamageComponent}, - comp::{ - CharacterState, EnergyChange, EnergySource, MeleeAttack, PoiseChange, PoiseSource, - StateUpdate, + combat::{ + Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, }, + comp::{CharacterState, EnergyChange, EnergySource, MeleeAttack, StateUpdate}, consts::GRAVITY, states::{ behavior::{CharacterBehavior, JoinData}, @@ -120,6 +119,9 @@ impl CharacterBehavior for Data { source: DamageSource::Melee, value: self.static_data.base_damage as f32, }; + let poise = AttackEffect::Poise(self.static_data.base_poise_damage as f32); + let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) + .with_requirement(CombatRequirement::AnyDamage); let knockback = AttackEffect::Knockback(Knockback { strength: self.static_data.knockback, direction: KnockbackDir::Away, @@ -128,7 +130,10 @@ impl CharacterBehavior for Data { let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(knockback) .with_effect(buff); - let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3); + let attack = Attack::default() + .with_damage(damage) + .with_crit(0.5, 1.3) + .with_effect(poise); // Hit attempt data.updater.insert(data.entity, MeleeAttack { From 2b11ae6569fd8c3591d34acec6daee61a9103554 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 30 Jan 2021 00:03:23 -0500 Subject: [PATCH 15/23] Attacks can now heal. --- common/src/combat.rs | 26 +++++++++++++++++++++++++- common/src/states/basic_beam.rs | 13 ++++++++----- server/src/events/entity_creation.rs | 2 +- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index d2d59948e0..2ef0a1f0fc 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -148,6 +148,18 @@ impl Attack { kb_dir: *dir, }); }, + AttackEffect::Heal(h) => { + let change = HealthChange { + amount: *h as i32, + cause: HealthSource::Heal { + by: Some(attacker_uid), + }, + }; + server_events.push(ServerEvent::Damage { + entity: target_entity, + change, + }); + }, } } } @@ -211,6 +223,18 @@ impl Attack { kb_dir: *dir, }); }, + AttackEffect::Heal(h) => { + let change = HealthChange { + amount: h as i32, + cause: HealthSource::Heal { + by: Some(attacker_uid), + }, + }; + server_events.push(ServerEvent::Damage { + entity: target_entity, + change, + }); + }, } } } @@ -264,7 +288,7 @@ impl EffectComponent { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum AttackEffect { - //Heal(f32), + Heal(f32), Buff(CombatBuff), Knockback(Knockback), EnergyReward(u32), diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 5c367e38d1..b4de42bf78 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -126,10 +126,8 @@ impl CharacterBehavior for Data { source: DamageSource::Energy, value: self.static_data.base_dps as f32 / self.static_data.tick_rate, }; - let heal = Damage { - source: DamageSource::Healing, - value: self.static_data.base_hps as f32 / self.static_data.tick_rate, - }; + let heal = self.static_data.base_hps as f32 / self.static_data.tick_rate; + let heal = AttackEffect::Heal(heal); let speed = self.static_data.range / self.static_data.beam_duration.as_secs_f32(); @@ -139,7 +137,12 @@ impl CharacterBehavior for Data { let lifesteal = AttackEffect::Lifesteal(self.static_data.lifesteal_eff); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(lifesteal); - let attack = Attack::default().with_damage(damage).with_effect(energy); + let heal = EffectComponent::new(Some(GroupTarget::InGroup), heal) + /*.with_requirement(CombatRequirement::SufficientEnergy(self.static_data.energy_cost))*/; + let attack = Attack::default() + .with_damage(damage) + .with_effect(energy) + .with_effect(heal); let properties = beam::Properties { attack, diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index ca4930eb95..1464a420c8 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -170,7 +170,7 @@ pub fn handle_beam(server: &mut Server, properties: beam::Properties, pos: Pos, let ecs = state.ecs(); ecs.write_resource::>().push(Outcome::Beam { pos: pos.0, - heal: false, //properties.lifesteal_eff > 0.0, + heal: false, //properties.lifesteal_eff > 0.0, // Fix before merging }); state.create_beam(properties, pos, ori).build(); } From e5caef8a5438f23af27a0de2ad916dcfbea7d977 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 30 Jan 2021 00:17:40 -0500 Subject: [PATCH 16/23] Effects on attacks can now require energy. --- common/src/combat.rs | 27 ++++++++++--- common/src/states/basic_beam.rs | 4 +- common/sys/src/beam.rs | 72 +-------------------------------- common/sys/src/melee.rs | 5 ++- common/sys/src/projectile.rs | 7 +++- 5 files changed, 36 insertions(+), 79 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 2ef0a1f0fc..3d699c019c 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -11,7 +11,8 @@ use crate::{ }, poise::PoiseChange, skills::{SkillGroupKind, SkillSet}, - Body, EnergyChange, EnergySource, Health, HealthChange, HealthSource, Inventory, Stats, + Body, Energy, EnergyChange, EnergySource, Health, HealthChange, HealthSource, Inventory, + Stats, }, event::ServerEvent, uid::Uid, @@ -71,8 +72,9 @@ impl Attack { target_group: GroupTarget, attacker_entity: EcsEntity, target_entity: EcsEntity, - inventory: Option<&Inventory>, + target_inventory: Option<&Inventory>, attacker_uid: Uid, + attacker_energy: Option<&Energy>, dir: Dir, target_dodging: bool, ) -> Vec { @@ -86,7 +88,7 @@ impl Attack { .filter(|d| !(matches!(d.target, Some(GroupTarget::OutOfGroup)) && target_dodging)) { let change = damage.damage.modify_damage( - inventory, + target_inventory, Some(attacker_uid), is_crit, self.crit_multiplier, @@ -141,7 +143,7 @@ impl Attack { }); }, AttackEffect::Poise(p) => { - let change = PoiseChange::from_attack(*p, inventory); + let change = PoiseChange::from_attack(*p, target_inventory); server_events.push(ServerEvent::PoiseChange { entity: target_entity, change, @@ -172,6 +174,20 @@ impl Attack { { if match &effect.requirement { Some(CombatRequirement::AnyDamage) => accumulated_damage != 0.0, + Some(CombatRequirement::SufficientEnergy(r)) => { + if attacker_energy.map_or(true, |e| e.current() >= *r) { + server_events.push(ServerEvent::EnergyChange { + entity: attacker_entity, + change: EnergyChange { + amount: -(*r as i32), + source: EnergySource::Ability, + }, + }); + true + } else { + false + } + }, None => true, } { match effect.effect { @@ -216,7 +232,7 @@ impl Attack { }); }, AttackEffect::Poise(p) => { - let change = PoiseChange::from_attack(p, inventory); + let change = PoiseChange::from_attack(p, target_inventory); server_events.push(ServerEvent::PoiseChange { entity: target_entity, change, @@ -299,6 +315,7 @@ pub enum AttackEffect { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum CombatRequirement { AnyDamage, + SufficientEnergy(u32), } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index b4de42bf78..ccaab77a04 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -138,7 +138,9 @@ impl CharacterBehavior for Data { let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) .with_effect(lifesteal); let heal = EffectComponent::new(Some(GroupTarget::InGroup), heal) - /*.with_requirement(CombatRequirement::SufficientEnergy(self.static_data.energy_cost))*/; + .with_requirement(CombatRequirement::SufficientEnergy( + self.static_data.energy_cost, + )); let attack = Attack::default() .with_damage(damage) .with_effect(energy) diff --git a/common/sys/src/beam.rs b/common/sys/src/beam.rs index 6fa408b253..96d50d8c81 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -175,6 +175,7 @@ impl<'a> System<'a> for Sys { b, inventory_b_maybe, owner_uid, + energies.get(beam_owner), ori.0, false, ); @@ -187,77 +188,6 @@ impl<'a> System<'a> for Sys { server_emitter.emit(event); } } - - // for (target, damage) in beam_segment.damages.iter() { - // if let Some(target) = target { - // if *target != target_group { - // continue; - // } - // } - - // // Modify damage - // let change = damage.modify_damage( - // inventories.get(b), - // beam_segment.owner, - // false, - // 0.0, - // ); - - // match target { - // Some(GroupTarget::OutOfGroup) => { - // server_emitter.emit(ServerEvent::Damage { - // entity: b, change }); if - // let Some(entity) = beam_owner { - // server_emitter.emit(ServerEvent::Damage { - // entity, - // change: HealthChange { - // amount: (-change.amount as f32 - // * beam_segment.lifesteal_eff) - // as i32, - // cause: HealthSource::Heal { - // by: beam_segment.owner, - // }, - // }, - // }); - // - // server_emitter.emit(ServerEvent::EnergyChange { - // entity, - // change: EnergyChange { - // amount: beam_segment.energy_regen - // as i32, - // source: EnergySource::HitEnemy, - // }, - // }); - // } - // }, - // Some(GroupTarget::InGroup) => { - // if let Some(energy) = beam_owner.and_then(|o| - // energies.get(o)) { if - // energy.current() > beam_segment.energy_cost { - // - // server_emitter.emit(ServerEvent::EnergyChange { - // entity: beam_owner.unwrap(), /* - // If it's able to get an energy - // * component, the entity exists */ - // change: EnergyChange { - // amount: - // -(beam_segment.energy_cost as i32), // Stamina use - // source: - // EnergySource::Ability, - // }, }); - // server_emitter - // .emit(ServerEvent::Damage { - // entity: b, change }); - // } } - // }, - // None => {}, - // } - - // // Adds entities that were hit to the hit_entities - // list on the beam, sees if // it needs - // to purge the hit_entities list - // hit_entities.push(*uid_b); - // } } } } diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index 675e5f5a92..187cff1f24 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -1,5 +1,5 @@ use common::{ - comp::{group, Body, CharacterState, Health, Inventory, MeleeAttack, Ori, Pos, Scale}, + comp::{group, Body, CharacterState, Energy, Health, Inventory, MeleeAttack, Ori, Pos, Scale}, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, span, @@ -26,6 +26,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Scale>, ReadStorage<'a, Body>, ReadStorage<'a, Health>, + ReadStorage<'a, Energy>, ReadStorage<'a, Inventory>, ReadStorage<'a, group::Group>, WriteStorage<'a, MeleeAttack>, @@ -45,6 +46,7 @@ impl<'a> System<'a> for Sys { scales, bodies, healths, + energies, inventories, groups, mut attacking_storage, @@ -133,6 +135,7 @@ impl<'a> System<'a> for Sys { b, inventory_b_maybe, *uid, + energies.get(entity), dir, is_dodge, ); diff --git a/common/sys/src/projectile.rs b/common/sys/src/projectile.rs index bae042cecd..06ea65802c 100644 --- a/common/sys/src/projectile.rs +++ b/common/sys/src/projectile.rs @@ -1,5 +1,7 @@ use common::{ - comp::{projectile, Group, HealthSource, Inventory, Ori, PhysicsState, Pos, Projectile, Vel}, + comp::{ + projectile, Energy, Group, HealthSource, Inventory, Ori, PhysicsState, Pos, Projectile, Vel, + }, event::{EventBus, ServerEvent}, metrics::SysMetrics, resources::DeltaTime, @@ -29,6 +31,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Projectile>, ReadStorage<'a, Inventory>, ReadStorage<'a, Group>, + ReadStorage<'a, Energy>, ); fn run( @@ -46,6 +49,7 @@ impl<'a> System<'a> for Sys { mut projectiles, inventories, groups, + energies, ): Self::SystemData, ) { let start_time = std::time::Instant::now(); @@ -110,6 +114,7 @@ impl<'a> System<'a> for Sys { target_entity, inventories.get(target_entity), owner, + energies.get(owner_entity), ori.0, false, ); From 7675e537406f3584291ba274eeb1fc73dd3b4ce1 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 30 Jan 2021 12:11:57 -0500 Subject: [PATCH 17/23] Shockwaves now use attacks. --- common/src/comp/shockwave.rs | 5 ++-- common/src/states/shockwave.rs | 31 ++++++++++++---------- common/sys/src/shockwave.rs | 48 ++++++++++++++++------------------ 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs index cae93eb73c..50c1fd1412 100644 --- a/common/src/comp/shockwave.rs +++ b/common/src/comp/shockwave.rs @@ -1,4 +1,4 @@ -use crate::{comp::PoiseChange, uid::Uid, Damage, GroupTarget, Knockback}; +use crate::{combat::Attack, uid::Uid}; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; use specs_idvs::IdvStorage; @@ -9,8 +9,7 @@ pub struct Properties { pub angle: f32, pub vertical_angle: f32, pub speed: f32, - pub effects: Vec<(Option, Damage, PoiseChange)>, - pub knockback: Knockback, + pub attack: Attack, pub requires_ground: bool, pub duration: Duration, pub owner: Option, diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index f166ad5444..38dcc213e1 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -1,11 +1,14 @@ use crate::{ - comp::{shockwave, CharacterState, PoiseChange, PoiseSource, StateUpdate}, + combat::{ + Attack, AttackEffect, CombatRequirement, Damage, DamageComponent, DamageSource, + EffectComponent, GroupTarget, Knockback, + }, + comp::{shockwave, CharacterState, StateUpdate}, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, - Damage, DamageSource, GroupTarget, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -80,23 +83,23 @@ impl CharacterBehavior for Data { }); } else { // Attack + let damage = Damage { + source: DamageSource::Shockwave, + value: self.static_data.damage as f32, + }; + let poise = AttackEffect::Poise(self.static_data.poise_damage as f32); + let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) + .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::Knockback(self.static_data.knockback); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + .with_effect(knockback); + let attack = Attack::default().with_damage(damage).with_effect(poise); let properties = shockwave::Properties { angle: self.static_data.shockwave_angle, vertical_angle: self.static_data.shockwave_vertical_angle, speed: self.static_data.shockwave_speed, duration: self.static_data.shockwave_duration, - effects: vec![( - Some(GroupTarget::OutOfGroup), - Damage { - source: DamageSource::Shockwave, - value: self.static_data.damage as f32, - }, - PoiseChange { - amount: -(self.static_data.poise_damage as i32), - source: PoiseSource::Attack, - }, - )], - knockback: self.static_data.knockback, + attack, requires_ground: self.static_data.requires_ground, owner: Some(*data.uid), }; diff --git a/common/sys/src/shockwave.rs b/common/sys/src/shockwave.rs index 7602aa7286..fa66534cc1 100644 --- a/common/sys/src/shockwave.rs +++ b/common/sys/src/shockwave.rs @@ -1,6 +1,6 @@ use common::{ comp::{ - group, Body, Health, HealthSource, Inventory, Last, Ori, PhysicsState, Pos, Scale, + group, Body, Energy, Health, HealthSource, Inventory, Last, Ori, PhysicsState, Pos, Scale, Shockwave, ShockwaveHitEntities, }, event::{EventBus, LocalEvent, ServerEvent}, @@ -36,6 +36,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, PhysicsState>, WriteStorage<'a, Shockwave>, WriteStorage<'a, ShockwaveHitEntities>, + ReadStorage<'a, Energy>, ); fn run( @@ -59,6 +60,7 @@ impl<'a> System<'a> for Sys { physics_states, mut shockwaves, mut shockwave_hit_lists, + energies, ): Self::SystemData, ) { let mut server_emitter = server_bus.emitter(); @@ -114,11 +116,13 @@ impl<'a> System<'a> for Sys { end: frame_end_dist, }; + let shockwave_owner = shockwave + .owner + .and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into())); + // Group to ignore collisions with // Might make this more nuanced if shockwaves are used for non damage effects - let group = shockwave - .owner - .and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into())) + let group = shockwave_owner .and_then(|e| groups.get(e)); // Go through all other effectable entities @@ -191,31 +195,25 @@ impl<'a> System<'a> for Sys { && (!shockwave.requires_ground || physics_state_b.on_ground); if hit { - for (target, damage, poise_damage) in shockwave.effects.iter() { - if let Some(target) = target { - if *target != target_group { - continue; - } - } + let dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - let owner_uid = shockwave.owner.unwrap_or(*uid); - let poise_change = poise_damage.modify_poise_damage(inventories.get(b)); - let change = - damage.modify_damage(inventories.get(b), Some(owner_uid), false, 0.0); + let server_events = shockwave.properties.attack.apply_attack( + target_group, + shockwave_owner.unwrap_or(entity), + b, + inventories.get(b), + shockwave.owner.unwrap_or(*uid), + shockwave_owner.and_then(|e| energies.get(e)), + dir, + false, + ); - server_emitter.emit(ServerEvent::Damage { entity: b, change }); + if !server_events.is_empty() { shockwave_hit_list.hit_entities.push(*uid_b); + } - let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - let impulse = shockwave.knockback.calculate_impulse(kb_dir); - if !impulse.is_approx_zero() { - server_emitter.emit(ServerEvent::Knockback { entity: b, impulse }); - } - server_emitter.emit(ServerEvent::PoiseChange { - entity: b, - change: poise_change, - kb_dir: *kb_dir, - }); + for event in server_events { + server_emitter.emit(event); } } } From 5c16b0b532e5d0cb6f58cba7fcf7387762faa28e Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 30 Jan 2021 17:35:00 -0500 Subject: [PATCH 18/23] Explosions now use attacks. --- common/src/combat.rs | 7 +- common/src/comp/projectile.rs | 130 +++++++-------------- common/src/explosion.rs | 10 +- common/sys/src/shockwave.rs | 3 +- server/src/cmd.rs | 12 +- server/src/events/entity_manipulation.rs | 139 +++++++++++++---------- server/src/sys/object.rs | 46 +++----- 7 files changed, 147 insertions(+), 200 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 3d699c019c..d5ce0ad571 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -173,7 +173,7 @@ impl Attack { .filter(|e| !(matches!(e.target, Some(GroupTarget::OutOfGroup)) && target_dodging)) { if match &effect.requirement { - Some(CombatRequirement::AnyDamage) => accumulated_damage != 0.0, + Some(CombatRequirement::AnyDamage) => accumulated_damage > 0.0, Some(CombatRequirement::SufficientEnergy(r)) => { if attacker_energy.map_or(true, |e| e.current() >= *r) { server_events.push(ServerEvent::EnergyChange { @@ -322,7 +322,6 @@ pub enum CombatRequirement { pub enum DamageSource { Buff(BuffKind), Melee, - Healing, Projectile, Explosion, Falling, @@ -444,10 +443,6 @@ impl Damage { }, } }, - DamageSource::Healing => HealthChange { - amount: damage as i32, - cause: HealthSource::Heal { by: uid }, - }, DamageSource::Falling => { // Armor if (damage_reduction - 1.0).abs() < f32::EPSILON { diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 06bb97f7e3..5cb2326593 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -3,7 +3,6 @@ use crate::{ Attack, AttackEffect, CombatBuff, CombatRequirement, Damage, DamageComponent, DamageSource, EffectComponent, GroupTarget, Knockback, KnockbackDir, }, - effect, uid::Uid, Explosion, RadiusEffect, }; @@ -109,41 +108,30 @@ impl ProjectileConstructor { damage, radius, energy_regen, - } => Projectile { - hit_solid: vec![ - Effect::Explode(Explosion { - effects: vec![ - RadiusEffect::Entity( - Some(GroupTarget::OutOfGroup), - effect::Effect::Damage(Damage { - source: DamageSource::Explosion, - value: damage, - }), - ), - RadiusEffect::TerrainDestruction(2.0), - ], - radius, - energy_regen, - }), - Effect::Vanish, - ], - hit_entity: vec![ - Effect::Explode(Explosion { - effects: vec![RadiusEffect::Entity( - Some(GroupTarget::OutOfGroup), - effect::Effect::Damage(Damage { - source: DamageSource::Explosion, - value: damage, - }), - )], - radius, - energy_regen, - }), - Effect::Vanish, - ], - time_left: Duration::from_secs(10), - owner, - ignore_group: true, + } => { + let damage = Damage { + source: DamageSource::Explosion, + value: damage, + }; + let energy = AttackEffect::EnergyReward(energy_regen); + let energy = EffectComponent::new(None, energy) + .with_requirement(CombatRequirement::AnyDamage); + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); + let attack = Attack::default().with_damage(damage).with_effect(energy); + let explosion = Explosion { + effects: vec![ + RadiusEffect::Attack(attack), + RadiusEffect::TerrainDestruction(2.0), + ], + radius, + }; + Projectile { + hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish], + hit_entity: vec![Effect::Explode(explosion), Effect::Vanish], + time_left: Duration::from_secs(10), + owner, + ignore_group: true, + } }, Firebolt { damage, @@ -171,56 +159,26 @@ impl ProjectileConstructor { heal, damage, radius, - } => Projectile { - hit_solid: vec![ - Effect::Explode(Explosion { - effects: vec![ - RadiusEffect::Entity( - Some(GroupTarget::OutOfGroup), - effect::Effect::Damage(Damage { - source: DamageSource::Explosion, - value: damage, - }), - ), - RadiusEffect::Entity( - Some(GroupTarget::InGroup), - effect::Effect::Damage(Damage { - source: DamageSource::Healing, - value: heal, - }), - ), - ], - radius, - energy_regen: 0, - }), - Effect::Vanish, - ], - hit_entity: vec![ - Effect::Explode(Explosion { - effects: vec![ - RadiusEffect::Entity( - Some(GroupTarget::OutOfGroup), - effect::Effect::Damage(Damage { - source: DamageSource::Explosion, - value: damage, - }), - ), - RadiusEffect::Entity( - Some(GroupTarget::InGroup), - effect::Effect::Damage(Damage { - source: DamageSource::Healing, - value: heal, - }), - ), - ], - radius, - energy_regen: 0, - }), - Effect::Vanish, - ], - time_left: Duration::from_secs(10), - owner, - ignore_group: true, + } => { + let damage = Damage { + source: DamageSource::Explosion, + value: damage, + }; + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); + let heal = AttackEffect::Heal(heal); + let heal = EffectComponent::new(Some(GroupTarget::InGroup), heal); + let attack = Attack::default().with_damage(damage).with_effect(heal); + let explosion = Explosion { + effects: vec![RadiusEffect::Attack(attack)], + radius, + }; + Projectile { + hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish], + hit_entity: vec![Effect::Explode(explosion), Effect::Vanish], + time_left: Duration::from_secs(10), + owner, + ignore_group: false, + } }, Possess => Projectile { hit_solid: vec![Effect::Stick], diff --git a/common/src/explosion.rs b/common/src/explosion.rs index 889602ecd2..04296a856b 100644 --- a/common/src/explosion.rs +++ b/common/src/explosion.rs @@ -1,15 +1,15 @@ -use crate::{combat::GroupTarget, effect::Effect}; +use crate::{combat::Attack, effect::Effect}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Explosion { pub effects: Vec, pub radius: f32, - pub energy_regen: u32, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum RadiusEffect { TerrainDestruction(f32), - Entity(Option, Effect), + Entity(Effect), + Attack(Attack), } diff --git a/common/sys/src/shockwave.rs b/common/sys/src/shockwave.rs index fa66534cc1..d8608de45e 100644 --- a/common/sys/src/shockwave.rs +++ b/common/sys/src/shockwave.rs @@ -122,8 +122,7 @@ impl<'a> System<'a> for Sys { // Group to ignore collisions with // Might make this more nuanced if shockwaves are used for non damage effects - let group = shockwave_owner - .and_then(|e| groups.get(e)); + let group = shockwave_owner.and_then(|e| groups.get(e)); // Go through all other effectable entities for ( diff --git a/server/src/cmd.rs b/server/src/cmd.rs index baa017914a..3bd8ead2a9 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1360,17 +1360,13 @@ fn handle_explosion( pos: pos.0, explosion: Explosion { effects: vec![ - RadiusEffect::Entity( - None, - Effect::Damage(Damage { - source: DamageSource::Explosion, - value: 100.0 * power, - }), - ), + RadiusEffect::Entity(Effect::Damage(Damage { + source: DamageSource::Explosion, + value: 100.0 * power, + })), RadiusEffect::TerrainDestruction(power), ], radius: 3.0 * power, - energy_regen: 0, }, owner: ecs.read_storage::().get(target).copied(), reagent: None, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 15d5d4866c..39228ae9c2 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -16,12 +16,13 @@ use common::{ object, Alignment, Body, CharacterState, Energy, EnergyChange, Group, Health, HealthChange, HealthSource, Inventory, Item, Player, Poise, PoiseChange, PoiseSource, Pos, Stats, }, - effect::Effect, + event::{EventBus, ServerEvent}, lottery::Lottery, outcome::Outcome, rtsim::RtSimEntity, terrain::{Block, TerrainGrid}, uid::{Uid, UidAllocator}, + util::Dir, vol::ReadVol, Damage, DamageSource, Explosion, GroupTarget, RadiusEffect, }; @@ -576,32 +577,24 @@ pub fn handle_explosion( // Add an outcome // Uses radius as outcome power, makes negative if explosion has healing effect - let outcome_power = explosion.radius - * if explosion.effects.iter().any(|e| { - matches!( - e, - RadiusEffect::Entity( - _, - Effect::Damage(Damage { - source: DamageSource::Healing, - .. - }) - ) - ) - }) { - -1.0 - } else { - 1.0 - }; + let outcome_power = explosion.radius; + // + // * if explosion.effects.iter().any(|e| { matches!( e, RadiusEffect::Entity( _, + // Effect::Damage(Damage { source: DamageSource::Healing, .. }) ) ) + // }) { + // -1.0 + // } else { + // 1.0 + // }; ecs.write_resource::>() .push(Outcome::Explosion { pos, power: outcome_power, radius: explosion.radius, - is_attack: explosion - .effects - .iter() - .any(|e| matches!(e, RadiusEffect::Entity(_, Effect::Damage(_)))), + is_attack: false, /*explosion + .effects + .iter() + .any(|e| matches!(e, RadiusEffect::Entity(_, Effect::Damage(_))))*/ reagent, }); let owner_entity = owner.and_then(|uid| { @@ -685,54 +678,74 @@ pub fn handle_explosion( .cast(); } }, - RadiusEffect::Entity(target, mut effect) => { - for (entity_b, pos_b) in (&ecs.entities(), &ecs.read_storage::()).join() + RadiusEffect::Attack(attack) => { + // TODO: Before merging handle falloff + let energies = &ecs.read_storage::(); + for (entity_b, pos_b, _health_b, inventory_b_maybe) in ( + &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + ) + .join() + .filter(|(_, _, h, _)| !h.is_dead) { - // See if entities are in the same group - let mut same_group = owner_entity - .and_then(|e| groups.get(e)) - .map_or(false, |group_a| Some(group_a) == groups.get(entity_b)); - if let Some(entity) = owner_entity { - if entity == entity_b { - same_group = true; + // Check if it is a hit + let distance_squared = pos.distance_squared(pos_b.0); + let strength = 1.0 - distance_squared / explosion.radius.powi(2); + if strength > 0.0 { + // See if entities are in the same group + let same_group = owner_entity + .and_then(|e| groups.get(e)) + .map(|group_a| Some(group_a) == groups.get(entity_b)) + .unwrap_or(Some(entity_b) == owner_entity); + + let target_group = if same_group { + GroupTarget::InGroup + } else { + GroupTarget::OutOfGroup + }; + + let dir = Dir::new( + (pos_b.0 - pos) + .try_normalized() + .unwrap_or_else(Vec3::unit_z), + ); + + let server_events = attack.apply_attack( + target_group, + owner_entity.unwrap(), + entity_b, + inventory_b_maybe, + owner.unwrap(), + owner_entity.and_then(|e| energies.get(e)), + dir, + false, + ); + + let server_eventbus = ecs.read_resource::>(); + + for event in server_events { + server_eventbus.emit_now(event); } } - let target_group = if same_group { - GroupTarget::InGroup - } else { - GroupTarget::OutOfGroup - }; - - if let Some(target) = target { - if target != target_group { - continue; - } - } - + } + }, + RadiusEffect::Entity(mut effect) => { + for (entity_b, pos_b, _health_b) in ( + &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ) + .join() + .filter(|(_, _, h)| !h.is_dead) + { let distance_squared = pos.distance_squared(pos_b.0); let strength = 1.0 - distance_squared / explosion.radius.powi(2); if strength > 0.0 { - let is_alive = ecs - .read_storage::() - .get(entity_b) - .map_or(false, |h| !h.is_dead); - - if is_alive { - effect.modify_strength(strength); - server.state().apply_effect(entity_b, effect.clone(), owner); - // Apply energy change - if let Some(owner) = owner_entity { - if let Some(mut energy) = - ecs.write_storage::().get_mut(owner) - { - energy.change_by(EnergyChange { - amount: explosion.energy_regen as i32, - source: comp::EnergySource::HitEnemy, - }); - } - } - } + effect.modify_strength(strength); + server.state().apply_effect(entity_b, effect.clone(), owner); } } }, diff --git a/server/src/sys/object.rs b/server/src/sys/object.rs index ae577b40e0..afb3574a0f 100644 --- a/server/src/sys/object.rs +++ b/server/src/sys/object.rs @@ -49,24 +49,17 @@ impl<'a> System<'a> for Sys { pos: pos.0, explosion: Explosion { effects: vec![ - RadiusEffect::Entity( - None, - Effect::Damage(Damage { - source: DamageSource::Explosion, - value: 500.0, - }), - ), - RadiusEffect::Entity( - None, - Effect::PoiseChange(PoiseChange { - source: PoiseSource::Explosion, - amount: -100, - }), - ), + RadiusEffect::Entity(Effect::Damage(Damage { + source: DamageSource::Explosion, + value: 500.0, + })), + RadiusEffect::Entity(Effect::PoiseChange(PoiseChange { + source: PoiseSource::Explosion, + amount: -100, + })), RadiusEffect::TerrainDestruction(4.0), ], radius: 12.0, - energy_regen: 0, }, owner: *owner, reagent: None, @@ -83,24 +76,17 @@ impl<'a> System<'a> for Sys { pos: pos.0, explosion: Explosion { effects: vec![ - RadiusEffect::Entity( - None, - Effect::Damage(Damage { - source: DamageSource::Explosion, - value: 50.0, - }), - ), - RadiusEffect::Entity( - None, - Effect::PoiseChange(PoiseChange { - source: PoiseSource::Explosion, - amount: -40, - }), - ), + RadiusEffect::Entity(Effect::Damage(Damage { + source: DamageSource::Explosion, + value: 50.0, + })), + RadiusEffect::Entity(Effect::PoiseChange(PoiseChange { + source: PoiseSource::Explosion, + amount: -40, + })), RadiusEffect::TerrainDestruction(4.0), ], radius: 12.0, - energy_regen: 0, }, owner: *owner, reagent: Some(*reagent), From 5eec915c2e4f97c4859866acb62ce214856a0d7f Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 31 Jan 2021 11:40:04 -0500 Subject: [PATCH 19/23] Added support for damage falloff with explosions. --- common/src/combat.rs | 6 +++++- common/sys/src/beam.rs | 1 + common/sys/src/melee.rs | 1 + common/sys/src/projectile.rs | 1 + common/sys/src/shockwave.rs | 1 + server/src/events/entity_manipulation.rs | 4 ++-- server/src/state_ext.rs | 2 +- 7 files changed, 12 insertions(+), 4 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index d5ce0ad571..33ef2227ff 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -77,6 +77,8 @@ impl Attack { attacker_energy: Option<&Energy>, dir: Dir, target_dodging: bool, + // Currently just modifies damage, maybe look into modifying strength of other effects? + strength_modifier: f32, ) -> Vec { let is_crit = thread_rng().gen::() < self.crit_chance; let mut accumulated_damage = 0.0; @@ -92,6 +94,7 @@ impl Attack { Some(attacker_uid), is_crit, self.crit_multiplier, + strength_modifier, ); let damage_damage = -change.amount as f32; accumulated_damage += damage_damage; @@ -365,8 +368,9 @@ impl Damage { uid: Option, is_crit: bool, crit_mult: f32, + damage_modifier: f32, ) -> HealthChange { - let mut damage = self.value; + let mut damage = self.value * damage_modifier; let damage_reduction = inventory.map_or(0.0, |inv| Damage::compute_damage_reduction(inv)); match self.source { DamageSource::Melee => { diff --git a/common/sys/src/beam.rs b/common/sys/src/beam.rs index 96d50d8c81..8bbf4de88c 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -178,6 +178,7 @@ impl<'a> System<'a> for Sys { energies.get(beam_owner), ori.0, false, + 1.0, ); if !server_events.is_empty() { diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index 187cff1f24..6d3bbc3e7f 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -138,6 +138,7 @@ impl<'a> System<'a> for Sys { energies.get(entity), dir, is_dodge, + 1.0, ); if !server_events.is_empty() { diff --git a/common/sys/src/projectile.rs b/common/sys/src/projectile.rs index 06ea65802c..b83fe1d21f 100644 --- a/common/sys/src/projectile.rs +++ b/common/sys/src/projectile.rs @@ -117,6 +117,7 @@ impl<'a> System<'a> for Sys { energies.get(owner_entity), ori.0, false, + 1.0, ); for event in server_events { diff --git a/common/sys/src/shockwave.rs b/common/sys/src/shockwave.rs index d8608de45e..ec48344f70 100644 --- a/common/sys/src/shockwave.rs +++ b/common/sys/src/shockwave.rs @@ -205,6 +205,7 @@ impl<'a> System<'a> for Sys { shockwave_owner.and_then(|e| energies.get(e)), dir, false, + 1.0, ); if !server_events.is_empty() { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 39228ae9c2..a5f453c333 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -510,7 +510,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) source: DamageSource::Falling, value: falldmg, }; - let change = damage.modify_damage(inventories.get(entity), None, false, 0.0); + let change = damage.modify_damage(inventories.get(entity), None, false, 0.0, 1.0); health.change_by(change); } // Handle poise change @@ -679,7 +679,6 @@ pub fn handle_explosion( } }, RadiusEffect::Attack(attack) => { - // TODO: Before merging handle falloff let energies = &ecs.read_storage::(); for (entity_b, pos_b, _health_b, inventory_b_maybe) in ( &ecs.entities(), @@ -721,6 +720,7 @@ pub fn handle_explosion( owner_entity.and_then(|e| energies.get(e)), dir, false, + strength, ); let server_eventbus = ecs.read_resource::>(); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index da9d254a41..af04924032 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -90,7 +90,7 @@ impl StateExt for State { }, Effect::Damage(damage) => { let inventories = self.ecs().read_storage::(); - let change = damage.modify_damage(inventories.get(entity), source, false, 0.0); + let change = damage.modify_damage(inventories.get(entity), source, false, 0.0, 1.0); self.ecs() .write_storage::() .get_mut(entity) From 17d1432be0fcc361fc58f671eef58f52c2bea5e6 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 31 Jan 2021 12:15:28 -0500 Subject: [PATCH 20/23] Outcomes now correctly read if an attack had any healing components. --- common/src/combat.rs | 4 ++++ common/src/comp/beam.rs | 3 --- server/src/events/entity_creation.rs | 6 +++++- server/src/events/entity_manipulation.rs | 23 ++++++++++------------- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 33ef2227ff..a0e4ae075e 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -66,6 +66,8 @@ impl Attack { self } + pub fn effects(&self) -> impl Iterator { self.effects.iter() } + #[allow(clippy::too_many_arguments)] pub fn apply_attack( &self, @@ -303,6 +305,8 @@ impl EffectComponent { self.requirement = Some(requirement); self } + + pub fn effect(&self) -> &AttackEffect { &self.effect } } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/common/src/comp/beam.rs b/common/src/comp/beam.rs index f5d06266e2..60b722aed6 100644 --- a/common/src/comp/beam.rs +++ b/common/src/comp/beam.rs @@ -9,9 +9,6 @@ pub struct Properties { pub attack: Attack, pub angle: f32, pub speed: f32, - //pub lifesteal_eff: f32, - //pub energy_regen: u32, - //pub energy_cost: u32, pub duration: Duration, pub owner: Option, } diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 1464a420c8..052c5be8e5 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -1,6 +1,7 @@ use crate::{sys, Server, StateExt}; use common::{ character::CharacterId, + combat, comp::{ self, aura::{Aura, AuraKind, AuraTarget}, @@ -170,7 +171,10 @@ pub fn handle_beam(server: &mut Server, properties: beam::Properties, pos: Pos, let ecs = state.ecs(); ecs.write_resource::>().push(Outcome::Beam { pos: pos.0, - heal: false, //properties.lifesteal_eff > 0.0, // Fix before merging + heal: properties + .attack + .effects() + .any(|e| matches!(e.effect(), combat::AttackEffect::Heal(h) if *h > 0.0)), }); state.create_beam(properties, pos, ori).build(); } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index a5f453c333..1185f8c8fa 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -577,24 +577,21 @@ pub fn handle_explosion( // Add an outcome // Uses radius as outcome power, makes negative if explosion has healing effect - let outcome_power = explosion.radius; - // - // * if explosion.effects.iter().any(|e| { matches!( e, RadiusEffect::Entity( _, - // Effect::Damage(Damage { source: DamageSource::Healing, .. }) ) ) - // }) { - // -1.0 - // } else { - // 1.0 - // }; + let outcome_power = explosion.radius + * if explosion.effects.iter().any(|e| matches!(e, RadiusEffect::Attack(a) if a.effects().any(|e| matches!(e.effect(), combat::AttackEffect::Heal(h) if *h > 0.0)))) { + -1.0 + } else { + 1.0 + }; ecs.write_resource::>() .push(Outcome::Explosion { pos, power: outcome_power, radius: explosion.radius, - is_attack: false, /*explosion - .effects - .iter() - .any(|e| matches!(e, RadiusEffect::Entity(_, Effect::Damage(_))))*/ + is_attack: explosion + .effects + .iter() + .any(|e| matches!(e, RadiusEffect::Attack(_))), reagent, }); let owner_entity = owner.and_then(|uid| { From af982ec0bbdc22aa4123e41df8a7f4d27bece60b Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 31 Jan 2021 12:33:22 -0500 Subject: [PATCH 21/23] Changed knockback to be an effect on the overall attack, rather than a damage. --- common/src/combat.rs | 8 ++++---- common/src/comp/projectile.rs | 30 +++++++++++++++-------------- common/src/states/basic_melee.rs | 14 ++++++++------ common/src/states/charged_melee.rs | 16 ++++++++------- common/src/states/charged_ranged.rs | 22 ++++++++++++--------- common/src/states/combo_melee.rs | 14 ++++++++------ common/src/states/dash_melee.rs | 17 +++++++++------- common/src/states/leap_melee.rs | 14 ++++++++------ common/src/states/shockwave.rs | 16 +++++++++------ common/src/states/spin_melee.rs | 14 ++++++++------ 10 files changed, 94 insertions(+), 71 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index a0e4ae075e..b1e9be63d8 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -98,8 +98,8 @@ impl Attack { self.crit_multiplier, strength_modifier, ); - let damage_damage = -change.amount as f32; - accumulated_damage += damage_damage; + let applied_damage = -change.amount as f32; + accumulated_damage += applied_damage; if change.amount != 0 { server_events.push(ServerEvent::Damage { entity: target_entity, @@ -130,14 +130,14 @@ impl Attack { server_events.push(ServerEvent::Buff { entity: target_entity, buff_change: BuffChange::Add( - b.to_buff(attacker_uid, damage_damage), + b.to_buff(attacker_uid, applied_damage), ), }); } }, AttackEffect::Lifesteal(l) => { let change = HealthChange { - amount: (damage_damage * l) as i32, + amount: (applied_damage * l) as i32, cause: HealthSource::Heal { by: Some(attacker_uid), }, diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 5cb2326593..b35b512f44 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -76,25 +76,27 @@ impl ProjectileConstructor { knockback, energy_regen, } => { - let damage = Damage { - source: DamageSource::Projectile, - value: damage, - }; let knockback = AttackEffect::Knockback(Knockback { strength: knockback, direction: KnockbackDir::Away, }); + let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + .with_requirement(CombatRequirement::AnyDamage); let energy = AttackEffect::EnergyReward(energy_regen); let energy = EffectComponent::new(None, energy) .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_physical()); - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback) - .with_effect(buff); + let damage = Damage { + source: DamageSource::Projectile, + value: damage, + }; + let damage = + DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)).with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.2) - .with_effect(energy); + .with_effect(energy) + .with_effect(knockback); Projectile { hit_solid: vec![Effect::Stick], @@ -109,13 +111,13 @@ impl ProjectileConstructor { radius, energy_regen, } => { + let energy = AttackEffect::EnergyReward(energy_regen); + let energy = EffectComponent::new(None, energy) + .with_requirement(CombatRequirement::AnyDamage); let damage = Damage { source: DamageSource::Explosion, value: damage, }; - let energy = AttackEffect::EnergyReward(energy_regen); - let energy = EffectComponent::new(None, energy) - .with_requirement(CombatRequirement::AnyDamage); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); let attack = Attack::default().with_damage(damage).with_effect(energy); let explosion = Explosion { @@ -137,13 +139,13 @@ impl ProjectileConstructor { damage, energy_regen, } => { + let energy = AttackEffect::EnergyReward(energy_regen); + let energy = EffectComponent::new(None, energy) + .with_requirement(CombatRequirement::AnyDamage); let damage = Damage { source: DamageSource::Energy, value: damage, }; - let energy = AttackEffect::EnergyReward(energy_regen); - let energy = EffectComponent::new(None, energy) - .with_requirement(CombatRequirement::AnyDamage); let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); let attack = Attack::default().with_damage(damage).with_effect(energy); diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 5b1bd9b9c9..778d624fbb 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -92,10 +92,6 @@ impl CharacterBehavior for Data { ..*self }); - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32, - }; let poise = AttackEffect::Poise(self.static_data.base_poise_damage as f32); let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) .with_requirement(CombatRequirement::AnyDamage); @@ -103,18 +99,24 @@ impl CharacterBehavior for Data { strength: self.static_data.knockback, direction: KnockbackDir::Away, }); + let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + .with_requirement(CombatRequirement::AnyDamage); let energy = AttackEffect::EnergyReward(50); let energy = EffectComponent::new(None, energy) .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_physical()); + let damage = Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }; let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback) .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) .with_effect(energy) - .with_effect(poise); + .with_effect(poise) + .with_effect(knockback); // Hit attempt data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 2558d133cb..d8144b96b0 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -155,11 +155,6 @@ impl CharacterBehavior for Data { exhausted: true, ..*self }); - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.initial_damage as f32 - + self.charge_amount * self.static_data.scaled_damage as f32, - }; let poise = self.static_data.initial_poise_damage as f32 + self.charge_amount * self.static_data.scaled_poise_damage as f32; let poise = AttackEffect::Poise(poise); @@ -171,14 +166,21 @@ impl CharacterBehavior for Data { strength: knockback, direction: KnockbackDir::Away, }); + let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_physical()); + let damage = Damage { + source: DamageSource::Melee, + value: self.static_data.initial_damage as f32 + + self.charge_amount * self.static_data.scaled_damage as f32, + }; let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback) .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) - .with_effect(poise); + .with_effect(poise) + .with_effect(knockback); // Hit attempt data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index f53f7b2f14..5759024143 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -1,7 +1,7 @@ use crate::{ combat::{ - Attack, AttackEffect, CombatBuff, Damage, DamageComponent, DamageSource, GroupTarget, - Knockback, KnockbackDir, + Attack, AttackEffect, CombatBuff, CombatRequirement, Damage, DamageComponent, DamageSource, + EffectComponent, GroupTarget, Knockback, KnockbackDir, }, comp::{ projectile, Body, CharacterState, EnergyChange, EnergySource, Gravity, LightEmitter, @@ -103,22 +103,26 @@ impl CharacterBehavior for Data { let charge_frac = (self.timer.as_secs_f32() / self.static_data.charge_duration.as_secs_f32()) .min(1.0); - let damage = Damage { - source: DamageSource::Projectile, - value: self.static_data.initial_damage as f32 - + charge_frac * self.static_data.scaled_damage as f32, - }; let knockback = self.static_data.initial_knockback + charge_frac * self.static_data.scaled_knockback; let knockback = AttackEffect::Knockback(Knockback { strength: knockback, direction: KnockbackDir::Away, }); + let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_physical()); + let damage = Damage { + source: DamageSource::Projectile, + value: self.static_data.initial_damage as f32 + + charge_frac * self.static_data.scaled_damage as f32, + }; let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback) .with_effect(buff); - let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.2); + let attack = Attack::default() + .with_damage(damage) + .with_crit(0.5, 1.2) + .with_effect(knockback); // Fire let projectile = Projectile { diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 85763ff24b..3a24c54151 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -185,14 +185,12 @@ impl CharacterBehavior for Data { let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) .with_requirement(CombatRequirement::AnyDamage); - let damage = Damage { - source: DamageSource::Melee, - value: damage as f32, - }; let knockback = AttackEffect::Knockback(Knockback { strength: self.static_data.stage_data[stage_index].knockback, direction: KnockbackDir::Away, }); + let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + .with_requirement(CombatRequirement::AnyDamage); let energy = self.static_data.max_energy_gain.min( self.static_data.initial_energy_gain + self.combo * self.static_data.energy_increase, @@ -201,14 +199,18 @@ impl CharacterBehavior for Data { let energy = EffectComponent::new(None, energy) .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_physical()); + let damage = Damage { + source: DamageSource::Melee, + value: damage as f32, + }; let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback) .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) .with_effect(energy) - .with_effect(poise); + .with_effect(poise) + .with_effect(knockback); data.updater.insert(data.entity, MeleeAttack { attack, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 93e73ec006..74191a1c42 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -132,11 +132,6 @@ impl CharacterBehavior for Data { if !self.exhausted { // Hit attempt (also checks if player is moving) if update.vel.0.distance_squared(Vec3::zero()) > 1.0 { - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32 - + charge_frac * self.static_data.scaled_damage as f32, - }; let poise = self.static_data.base_poise_damage as f32 + charge_frac * self.static_data.scaled_poise_damage as f32; let poise = AttackEffect::Poise(poise); @@ -148,15 +143,23 @@ impl CharacterBehavior for Data { strength: knockback, direction: KnockbackDir::Away, }); + let knockback = + EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_physical()); + let damage = Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32 + + charge_frac * self.static_data.scaled_damage as f32, + }; let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback) .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) - .with_effect(poise); + .with_effect(poise) + .with_effect(knockback); data.updater.insert(data.entity, MeleeAttack { attack, diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index d6bb8a2e9b..799bc37337 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -150,10 +150,6 @@ impl CharacterBehavior for Data { }, StageSection::Recover => { if !self.exhausted { - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32, - }; let poise = AttackEffect::Poise(self.static_data.base_poise_damage as f32); let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) .with_requirement(CombatRequirement::AnyDamage); @@ -161,14 +157,20 @@ impl CharacterBehavior for Data { strength: self.static_data.knockback, direction: KnockbackDir::Away, }); + let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_physical()); + let damage = Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }; let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback) .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) - .with_effect(poise); + .with_effect(poise) + .with_effect(knockback); // Hit attempt, when animation plays data.updater.insert(data.entity, MeleeAttack { diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index 38dcc213e1..2cb3ca6071 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -83,17 +83,21 @@ impl CharacterBehavior for Data { }); } else { // Attack - let damage = Damage { - source: DamageSource::Shockwave, - value: self.static_data.damage as f32, - }; let poise = AttackEffect::Poise(self.static_data.poise_damage as f32); let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) .with_requirement(CombatRequirement::AnyDamage); let knockback = AttackEffect::Knockback(self.static_data.knockback); - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) + let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + .with_requirement(CombatRequirement::AnyDamage); + let damage = Damage { + source: DamageSource::Shockwave, + value: self.static_data.damage as f32, + }; + let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); + let attack = Attack::default() + .with_damage(damage) + .with_effect(poise) .with_effect(knockback); - let attack = Attack::default().with_damage(damage).with_effect(poise); let properties = shockwave::Properties { angle: self.static_data.shockwave_angle, vertical_angle: self.static_data.shockwave_vertical_angle, diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index dfd2aa677b..ca0b1f6220 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -115,10 +115,6 @@ impl CharacterBehavior for Data { ..*self }); - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32, - }; let poise = AttackEffect::Poise(self.static_data.base_poise_damage as f32); let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) .with_requirement(CombatRequirement::AnyDamage); @@ -126,14 +122,20 @@ impl CharacterBehavior for Data { strength: self.static_data.knockback, direction: KnockbackDir::Away, }); + let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + .with_requirement(CombatRequirement::AnyDamage); let buff = AttackEffect::Buff(CombatBuff::default_physical()); + let damage = Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }; let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(knockback) .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) - .with_effect(poise); + .with_effect(poise) + .with_effect(knockback); // Hit attempt data.updater.insert(data.entity, MeleeAttack { From 80954f3ba49254906980f743926270811d24e3ae Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 31 Jan 2021 21:43:26 -0500 Subject: [PATCH 22/23] Made attacker entity and uid optional to remove potential for attacks to not be applied and some unwraps. --- common/src/combat.rs | 111 ++++++++++++----------- common/sys/src/beam.rs | 34 ++++--- common/sys/src/melee.rs | 4 +- common/sys/src/projectile.rs | 38 ++++---- common/sys/src/shockwave.rs | 7 +- server/src/events/entity_manipulation.rs | 4 +- 6 files changed, 101 insertions(+), 97 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index b1e9be63d8..4dae84f8a7 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -72,10 +72,10 @@ impl Attack { pub fn apply_attack( &self, target_group: GroupTarget, - attacker_entity: EcsEntity, + attacker_entity: Option, target_entity: EcsEntity, target_inventory: Option<&Inventory>, - attacker_uid: Uid, + attacker_uid: Option, attacker_energy: Option<&Energy>, dir: Dir, target_dodging: bool, @@ -93,7 +93,7 @@ impl Attack { { let change = damage.damage.modify_damage( target_inventory, - Some(attacker_uid), + attacker_uid, is_crit, self.crit_multiplier, strength_modifier, @@ -117,13 +117,15 @@ impl Attack { } }, AttackEffect::EnergyReward(ec) => { - server_events.push(ServerEvent::EnergyChange { - entity: attacker_entity, - change: EnergyChange { - amount: *ec as i32, - source: EnergySource::HitEnemy, - }, - }); + if let Some(attacker_entity) = attacker_entity { + server_events.push(ServerEvent::EnergyChange { + entity: attacker_entity, + change: EnergyChange { + amount: *ec as i32, + source: EnergySource::HitEnemy, + }, + }); + } }, AttackEffect::Buff(b) => { if thread_rng().gen::() < b.chance { @@ -136,16 +138,16 @@ impl Attack { } }, AttackEffect::Lifesteal(l) => { - let change = HealthChange { - amount: (applied_damage * l) as i32, - cause: HealthSource::Heal { - by: Some(attacker_uid), - }, - }; - server_events.push(ServerEvent::Damage { - entity: attacker_entity, - change, - }); + if let Some(attacker_entity) = attacker_entity { + let change = HealthChange { + amount: (applied_damage * l) as i32, + cause: HealthSource::Heal { by: attacker_uid }, + }; + server_events.push(ServerEvent::Damage { + entity: attacker_entity, + change, + }); + } }, AttackEffect::Poise(p) => { let change = PoiseChange::from_attack(*p, target_inventory); @@ -158,9 +160,7 @@ impl Attack { AttackEffect::Heal(h) => { let change = HealthChange { amount: *h as i32, - cause: HealthSource::Heal { - by: Some(attacker_uid), - }, + cause: HealthSource::Heal { by: attacker_uid }, }; server_events.push(ServerEvent::Damage { entity: target_entity, @@ -181,13 +181,15 @@ impl Attack { Some(CombatRequirement::AnyDamage) => accumulated_damage > 0.0, Some(CombatRequirement::SufficientEnergy(r)) => { if attacker_energy.map_or(true, |e| e.current() >= *r) { - server_events.push(ServerEvent::EnergyChange { - entity: attacker_entity, - change: EnergyChange { - amount: -(*r as i32), - source: EnergySource::Ability, - }, - }); + if let Some(attacker_entity) = attacker_entity { + server_events.push(ServerEvent::EnergyChange { + entity: attacker_entity, + change: EnergyChange { + amount: -(*r as i32), + source: EnergySource::Ability, + }, + }); + } true } else { false @@ -206,13 +208,15 @@ impl Attack { } }, AttackEffect::EnergyReward(ec) => { - server_events.push(ServerEvent::EnergyChange { - entity: attacker_entity, - change: EnergyChange { - amount: ec as i32, - source: EnergySource::HitEnemy, - }, - }); + if let Some(attacker_entity) = attacker_entity { + server_events.push(ServerEvent::EnergyChange { + entity: attacker_entity, + change: EnergyChange { + amount: ec as i32, + source: EnergySource::HitEnemy, + }, + }); + } }, AttackEffect::Buff(b) => { if thread_rng().gen::() < b.chance { @@ -225,16 +229,16 @@ impl Attack { } }, AttackEffect::Lifesteal(l) => { - let change = HealthChange { - amount: (accumulated_damage * l) as i32, - cause: HealthSource::Heal { - by: Some(attacker_uid), - }, - }; - server_events.push(ServerEvent::Damage { - entity: attacker_entity, - change, - }); + if let Some(attacker_entity) = attacker_entity { + let change = HealthChange { + amount: (accumulated_damage * l) as i32, + cause: HealthSource::Heal { by: attacker_uid }, + }; + server_events.push(ServerEvent::Damage { + entity: attacker_entity, + change, + }); + } }, AttackEffect::Poise(p) => { let change = PoiseChange::from_attack(p, target_inventory); @@ -247,9 +251,7 @@ impl Attack { AttackEffect::Heal(h) => { let change = HealthChange { amount: h as i32, - cause: HealthSource::Heal { - by: Some(attacker_uid), - }, + cause: HealthSource::Heal { by: attacker_uid }, }; server_events.push(ServerEvent::Damage { entity: target_entity, @@ -542,8 +544,13 @@ impl CombatBuffStrength { } impl CombatBuff { - fn to_buff(self, uid: Uid, damage: f32) -> Buff { + fn to_buff(self, uid: Option, damage: f32) -> Buff { // TODO: Generate BufCategoryId vec (probably requires damage overhaul?) + let source = if let Some(uid) = uid { + BuffSource::Character { by: uid } + } else { + BuffSource::Unknown + }; Buff::new( self.kind, BuffData::new( @@ -551,7 +558,7 @@ impl CombatBuff { Some(Duration::from_secs_f32(self.dur_secs)), ), Vec::new(), - BuffSource::Character { by: uid }, + source, ) } diff --git a/common/sys/src/beam.rs b/common/sys/src/beam.rs index 8bbf4de88c..5ebb3ccefa 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -168,26 +168,24 @@ impl<'a> System<'a> for Sys { continue; } - if let (Some(beam_owner), Some(owner_uid)) = (beam_owner, beam_segment.owner) { - let server_events = beam_segment.properties.attack.apply_attack( - target_group, - beam_owner, - b, - inventory_b_maybe, - owner_uid, - energies.get(beam_owner), - ori.0, - false, - 1.0, - ); + let server_events = beam_segment.properties.attack.apply_attack( + target_group, + beam_owner, + b, + inventory_b_maybe, + beam_segment.owner, + beam_owner.and_then(|e| energies.get(e)), + ori.0, + false, + 1.0, + ); - if !server_events.is_empty() { - hit_entities.push(*uid_b); - } + if !server_events.is_empty() { + hit_entities.push(*uid_b); + } - for event in server_events { - server_emitter.emit(event); - } + for event in server_events { + server_emitter.emit(event); } } } diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index 6d3bbc3e7f..b373ba5b70 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -131,10 +131,10 @@ impl<'a> System<'a> for Sys { let server_events = attack.attack.apply_attack( target_group, - entity, + Some(entity), b, inventory_b_maybe, - *uid, + Some(*uid), energies.get(entity), dir, is_dodge, diff --git a/common/sys/src/projectile.rs b/common/sys/src/projectile.rs index b83fe1d21f..c80986deb2 100644 --- a/common/sys/src/projectile.rs +++ b/common/sys/src/projectile.rs @@ -103,26 +103,26 @@ impl<'a> System<'a> for Sys { for effect in projectile.hit_entity.drain(..) { match effect { projectile::Effect::Attack(attack) => { - if let Some(owner) = projectile.owner { - if let (Some(owner_entity), Some(target_entity)) = ( - uid_allocator.retrieve_entity_internal(owner.into()), - uid_allocator.retrieve_entity_internal(other.into()), - ) { - let server_events = attack.apply_attack( - target_group, - owner_entity, - target_entity, - inventories.get(target_entity), - owner, - energies.get(owner_entity), - ori.0, - false, - 1.0, - ); + if let Some(target_entity) = + uid_allocator.retrieve_entity_internal(other.into()) + { + let owner_entity = projectile + .owner + .and_then(|u| uid_allocator.retrieve_entity_internal(u.into())); + let server_events = attack.apply_attack( + target_group, + owner_entity, + target_entity, + inventories.get(target_entity), + projectile.owner, + owner_entity.and_then(|e| energies.get(e)), + ori.0, + false, + 1.0, + ); - for event in server_events { - server_emitter.emit(event); - } + for event in server_events { + server_emitter.emit(event); } } }, diff --git a/common/sys/src/shockwave.rs b/common/sys/src/shockwave.rs index ec48344f70..f40be3f9d2 100644 --- a/common/sys/src/shockwave.rs +++ b/common/sys/src/shockwave.rs @@ -70,9 +70,8 @@ impl<'a> System<'a> for Sys { let dt = dt.0; // Shockwaves - for (entity, uid, pos, ori, shockwave, shockwave_hit_list) in ( + for (entity, pos, ori, shockwave, shockwave_hit_list) in ( &entities, - &uids, &positions, &orientations, &shockwaves, @@ -198,10 +197,10 @@ impl<'a> System<'a> for Sys { let server_events = shockwave.properties.attack.apply_attack( target_group, - shockwave_owner.unwrap_or(entity), + shockwave_owner, b, inventories.get(b), - shockwave.owner.unwrap_or(*uid), + shockwave.owner, shockwave_owner.and_then(|e| energies.get(e)), dir, false, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 1185f8c8fa..3041ae58ea 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -710,10 +710,10 @@ pub fn handle_explosion( let server_events = attack.apply_attack( target_group, - owner_entity.unwrap(), + owner_entity, entity_b, inventory_b_maybe, - owner.unwrap(), + owner, owner_entity.and_then(|e| energies.get(e)), dir, false, From be8df9aef653bfdb39397cbb4d8222191f6489d8 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 2 Feb 2021 13:02:40 -0500 Subject: [PATCH 23/23] Addressed comments. --- common/src/combat.rs | 215 +++++++++++++---------- common/src/comp/character_state.rs | 4 +- common/src/comp/mod.rs | 2 +- common/src/comp/poise.rs | 10 +- common/src/comp/projectile.rs | 88 +++++----- common/src/states/basic_beam.rs | 43 +++-- common/src/states/basic_melee.rs | 53 +++--- common/src/states/behavior.rs | 6 +- common/src/states/charged_melee.rs | 60 ++++--- common/src/states/charged_ranged.rs | 39 ++-- common/src/states/combo_melee.rs | 57 +++--- common/src/states/dash_melee.rs | 62 +++---- common/src/states/leap_melee.rs | 50 +++--- common/src/states/shockwave.rs | 32 ++-- common/src/states/spin_melee.rs | 50 +++--- common/sys/src/beam.rs | 25 +-- common/sys/src/character_behavior.rs | 6 +- common/sys/src/melee.rs | 26 +-- common/sys/src/projectile.rs | 69 ++------ common/sys/src/shockwave.rs | 25 +-- common/sys/src/state.rs | 2 +- server/src/events/entity_creation.rs | 2 +- server/src/events/entity_manipulation.rs | 37 ++-- server/src/state_ext.rs | 8 +- 24 files changed, 505 insertions(+), 466 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 4dae84f8a7..610925b447 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -30,10 +30,17 @@ pub enum GroupTarget { OutOfGroup, } +#[derive(Copy, Clone)] +pub struct AttackerInfo<'a> { + pub entity: EcsEntity, + pub uid: Uid, + pub energy: Option<&'a Energy>, +} + #[derive(Clone, Debug, Serialize, Deserialize)] // TODO: Yeet clone derive pub struct Attack { - damages: Vec, - effects: Vec, + damages: Vec, + effects: Vec, crit_chance: f32, crit_multiplier: f32, } @@ -50,50 +57,48 @@ impl Default for Attack { } impl Attack { - pub fn with_damage(mut self, damage: DamageComponent) -> Self { + pub fn with_damage(mut self, damage: AttackDamage) -> Self { self.damages.push(damage); self } - pub fn with_effect(mut self, effect: EffectComponent) -> Self { + pub fn with_effect(mut self, effect: AttackEffect) -> Self { self.effects.push(effect); self } - pub fn with_crit(mut self, cc: f32, cm: f32) -> Self { - self.crit_chance = cc; - self.crit_multiplier = cm; + pub fn with_crit(mut self, crit_chance: f32, crit_multiplier: f32) -> Self { + self.crit_chance = crit_chance; + self.crit_multiplier = crit_multiplier; self } - pub fn effects(&self) -> impl Iterator { self.effects.iter() } + pub fn effects(&self) -> impl Iterator { self.effects.iter() } #[allow(clippy::too_many_arguments)] pub fn apply_attack( &self, target_group: GroupTarget, - attacker_entity: Option, + attacker_info: Option, target_entity: EcsEntity, target_inventory: Option<&Inventory>, - attacker_uid: Option, - attacker_energy: Option<&Energy>, dir: Dir, target_dodging: bool, // Currently just modifies damage, maybe look into modifying strength of other effects? strength_modifier: f32, - ) -> Vec { + mut emit: impl FnMut(ServerEvent), + ) { let is_crit = thread_rng().gen::() < self.crit_chance; let mut accumulated_damage = 0.0; - let mut server_events = Vec::new(); for damage in self .damages .iter() .filter(|d| d.target.map_or(true, |t| t == target_group)) .filter(|d| !(matches!(d.target, Some(GroupTarget::OutOfGroup)) && target_dodging)) { - let change = damage.damage.modify_damage( + let change = damage.damage.calculate_health_change( target_inventory, - attacker_uid, + attacker_info.map(|a| a.uid), is_crit, self.crit_multiplier, strength_modifier, @@ -101,24 +106,24 @@ impl Attack { let applied_damage = -change.amount as f32; accumulated_damage += applied_damage; if change.amount != 0 { - server_events.push(ServerEvent::Damage { + emit(ServerEvent::Damage { entity: target_entity, change, }); for effect in damage.effects.iter() { match effect { - AttackEffect::Knockback(kb) => { + CombatEffect::Knockback(kb) => { let impulse = kb.calculate_impulse(dir); if !impulse.is_approx_zero() { - server_events.push(ServerEvent::Knockback { + emit(ServerEvent::Knockback { entity: target_entity, impulse, }); } }, - AttackEffect::EnergyReward(ec) => { - if let Some(attacker_entity) = attacker_entity { - server_events.push(ServerEvent::EnergyChange { + CombatEffect::EnergyReward(ec) => { + if let Some(attacker_entity) = attacker_info.map(|a| a.entity) { + emit(ServerEvent::EnergyChange { entity: attacker_entity, change: EnergyChange { amount: *ec as i32, @@ -127,45 +132,55 @@ impl Attack { }); } }, - AttackEffect::Buff(b) => { + CombatEffect::Buff(b) => { if thread_rng().gen::() < b.chance { - server_events.push(ServerEvent::Buff { + emit(ServerEvent::Buff { entity: target_entity, buff_change: BuffChange::Add( - b.to_buff(attacker_uid, applied_damage), + b.to_buff(attacker_info.map(|a| a.uid), applied_damage), ), }); } }, - AttackEffect::Lifesteal(l) => { - if let Some(attacker_entity) = attacker_entity { + CombatEffect::Lifesteal(l) => { + if let Some(attacker_entity) = attacker_info.map(|a| a.entity) { let change = HealthChange { amount: (applied_damage * l) as i32, - cause: HealthSource::Heal { by: attacker_uid }, + cause: HealthSource::Heal { + by: attacker_info.map(|a| a.uid), + }, }; - server_events.push(ServerEvent::Damage { - entity: attacker_entity, + if change.amount != 0 { + emit(ServerEvent::Damage { + entity: attacker_entity, + change, + }); + } + } + }, + CombatEffect::Poise(p) => { + let change = PoiseChange::from_value(*p, target_inventory); + if change.amount != 0 { + emit(ServerEvent::PoiseChange { + entity: target_entity, change, + kb_dir: *dir, }); } }, - AttackEffect::Poise(p) => { - let change = PoiseChange::from_attack(*p, target_inventory); - server_events.push(ServerEvent::PoiseChange { - entity: target_entity, - change, - kb_dir: *dir, - }); - }, - AttackEffect::Heal(h) => { + CombatEffect::Heal(h) => { let change = HealthChange { amount: *h as i32, - cause: HealthSource::Heal { by: attacker_uid }, + cause: HealthSource::Heal { + by: attacker_info.map(|a| a.uid), + }, }; - server_events.push(ServerEvent::Damage { - entity: target_entity, - change, - }); + if change.amount != 0 { + emit(ServerEvent::Damage { + entity: target_entity, + change, + }); + } }, } } @@ -180,17 +195,24 @@ impl Attack { if match &effect.requirement { Some(CombatRequirement::AnyDamage) => accumulated_damage > 0.0, Some(CombatRequirement::SufficientEnergy(r)) => { - if attacker_energy.map_or(true, |e| e.current() >= *r) { - if let Some(attacker_entity) = attacker_entity { - server_events.push(ServerEvent::EnergyChange { - entity: attacker_entity, + if let Some(AttackerInfo { + entity, + energy: Some(e), + .. + }) = attacker_info + { + let sufficient_energy = e.current() >= *r; + if sufficient_energy { + emit(ServerEvent::EnergyChange { + entity, change: EnergyChange { amount: -(*r as i32), source: EnergySource::Ability, }, }); } - true + + sufficient_energy } else { false } @@ -198,18 +220,18 @@ impl Attack { None => true, } { match effect.effect { - AttackEffect::Knockback(kb) => { + CombatEffect::Knockback(kb) => { let impulse = kb.calculate_impulse(dir); if !impulse.is_approx_zero() { - server_events.push(ServerEvent::Knockback { + emit(ServerEvent::Knockback { entity: target_entity, impulse, }); } }, - AttackEffect::EnergyReward(ec) => { - if let Some(attacker_entity) = attacker_entity { - server_events.push(ServerEvent::EnergyChange { + CombatEffect::EnergyReward(ec) => { + if let Some(attacker_entity) = attacker_info.map(|a| a.entity) { + emit(ServerEvent::EnergyChange { entity: attacker_entity, change: EnergyChange { amount: ec as i32, @@ -218,61 +240,70 @@ impl Attack { }); } }, - AttackEffect::Buff(b) => { + CombatEffect::Buff(b) => { if thread_rng().gen::() < b.chance { - server_events.push(ServerEvent::Buff { + emit(ServerEvent::Buff { entity: target_entity, buff_change: BuffChange::Add( - b.to_buff(attacker_uid, accumulated_damage), + b.to_buff(attacker_info.map(|a| a.uid), accumulated_damage), ), }); } }, - AttackEffect::Lifesteal(l) => { - if let Some(attacker_entity) = attacker_entity { + CombatEffect::Lifesteal(l) => { + if let Some(attacker_entity) = attacker_info.map(|a| a.entity) { let change = HealthChange { amount: (accumulated_damage * l) as i32, - cause: HealthSource::Heal { by: attacker_uid }, + cause: HealthSource::Heal { + by: attacker_info.map(|a| a.uid), + }, }; - server_events.push(ServerEvent::Damage { - entity: attacker_entity, + if change.amount != 0 { + emit(ServerEvent::Damage { + entity: attacker_entity, + change, + }); + } + } + }, + CombatEffect::Poise(p) => { + let change = PoiseChange::from_value(p, target_inventory); + if change.amount != 0 { + emit(ServerEvent::PoiseChange { + entity: target_entity, + change, + kb_dir: *dir, + }); + } + }, + CombatEffect::Heal(h) => { + let change = HealthChange { + amount: h as i32, + cause: HealthSource::Heal { + by: attacker_info.map(|a| a.uid), + }, + }; + if change.amount != 0 { + emit(ServerEvent::Damage { + entity: target_entity, change, }); } }, - AttackEffect::Poise(p) => { - let change = PoiseChange::from_attack(p, target_inventory); - server_events.push(ServerEvent::PoiseChange { - entity: target_entity, - change, - kb_dir: *dir, - }); - }, - AttackEffect::Heal(h) => { - let change = HealthChange { - amount: h as i32, - cause: HealthSource::Heal { by: attacker_uid }, - }; - server_events.push(ServerEvent::Damage { - entity: target_entity, - change, - }); - }, } } } - server_events } } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DamageComponent { +pub struct AttackDamage { damage: Damage, target: Option, - effects: Vec, + effects: Vec, } -impl DamageComponent { +impl AttackDamage { pub fn new(damage: Damage, target: Option) -> Self { Self { damage, @@ -281,21 +312,21 @@ impl DamageComponent { } } - pub fn with_effect(mut self, effect: AttackEffect) -> Self { + pub fn with_effect(mut self, effect: CombatEffect) -> Self { self.effects.push(effect); self } } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EffectComponent { +pub struct AttackEffect { target: Option, - effect: AttackEffect, + effect: CombatEffect, requirement: Option, } -impl EffectComponent { - pub fn new(target: Option, effect: AttackEffect) -> Self { +impl AttackEffect { + pub fn new(target: Option, effect: CombatEffect) -> Self { Self { target, effect, @@ -308,11 +339,11 @@ impl EffectComponent { self } - pub fn effect(&self) -> &AttackEffect { &self.effect } + pub fn effect(&self) -> &CombatEffect { &self.effect } } #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum AttackEffect { +pub enum CombatEffect { Heal(f32), Buff(CombatBuff), Knockback(Knockback), @@ -368,7 +399,7 @@ impl Damage { } } - pub fn modify_damage( + pub fn calculate_health_change( self, inventory: Option<&Inventory>, uid: Option, @@ -377,7 +408,7 @@ impl Damage { damage_modifier: f32, ) -> HealthChange { let mut damage = self.value * damage_modifier; - let damage_reduction = inventory.map_or(0.0, |inv| Damage::compute_damage_reduction(inv)); + let damage_reduction = inventory.map_or(0.0, Damage::compute_damage_reduction); match self.source { DamageSource::Melee => { // Critical hit diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index d0fb1541d1..eaeee7da8b 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -170,7 +170,7 @@ impl Component for CharacterState { } #[derive(Debug, Serialize, Deserialize)] -pub struct MeleeAttack { +pub struct Melee { pub attack: Attack, pub range: f32, pub max_angle: f32, @@ -178,6 +178,6 @@ pub struct MeleeAttack { pub hit_count: u32, } -impl Component for MeleeAttack { +impl Component for Melee { type Storage = VecStorage; } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 76e4438573..72dad9fa85 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -40,7 +40,7 @@ pub use buff::{ Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs, ModifierKind, }; -pub use character_state::{CharacterState, MeleeAttack, StateUpdate}; +pub use character_state::{CharacterState, Melee, StateUpdate}; pub use chat::{ ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg, }; diff --git a/common/src/comp/poise.rs b/common/src/comp/poise.rs index ab2ffb46f4..1e13be2f13 100644 --- a/common/src/comp/poise.rs +++ b/common/src/comp/poise.rs @@ -21,8 +21,7 @@ pub struct PoiseChange { impl PoiseChange { /// Alters poise damage as a result of armor poise damage reduction pub fn modify_poise_damage(self, inventory: Option<&Inventory>) -> PoiseChange { - let poise_damage_reduction = - inventory.map_or(0.0, |inv| Poise::compute_poise_damage_reduction(inv)); + let poise_damage_reduction = inventory.map_or(0.0, Poise::compute_poise_damage_reduction); let poise_damage = self.amount as f32 * (1.0 - poise_damage_reduction); // Add match on poise source when different calculations per source // are needed/wanted @@ -32,10 +31,9 @@ impl PoiseChange { } } - /// Creates a poise change from an attack - pub fn from_attack(poise_damage: f32, inventory: Option<&Inventory>) -> Self { - let poise_damage_reduction = - inventory.map_or(0.0, |inv| Poise::compute_poise_damage_reduction(inv)); + /// Creates a poise change from a float + pub fn from_value(poise_damage: f32, inventory: Option<&Inventory>) -> Self { + let poise_damage_reduction = inventory.map_or(0.0, Poise::compute_poise_damage_reduction); let poise_change = -poise_damage * (1.0 - poise_damage_reduction); Self { amount: poise_change as i32, diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index b35b512f44..ac80d554ff 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -1,7 +1,7 @@ use crate::{ combat::{ - Attack, AttackEffect, CombatBuff, CombatRequirement, Damage, DamageComponent, DamageSource, - EffectComponent, GroupTarget, Knockback, KnockbackDir, + Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement, Damage, + DamageSource, GroupTarget, Knockback, KnockbackDir, }, uid::Uid, Explosion, RadiusEffect, @@ -14,16 +14,10 @@ use std::time::Duration; #[derive(Debug, Serialize, Deserialize)] pub enum Effect { Attack(Attack), - //Knockback(Knockback), - //RewardEnergy(u32), Explode(Explosion), Vanish, Stick, Possess, - /* Buff { - * buff: BuffEffect, - * chance: Option, - * }, */ } #[derive(Debug, Serialize, Deserialize)] @@ -76,22 +70,25 @@ impl ProjectileConstructor { knockback, energy_regen, } => { - let knockback = AttackEffect::Knockback(Knockback { - strength: knockback, - direction: KnockbackDir::Away, - }); - let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(Knockback { + strength: knockback, + direction: KnockbackDir::Away, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); + let energy = AttackEffect::new(None, CombatEffect::EnergyReward(energy_regen)) .with_requirement(CombatRequirement::AnyDamage); - let energy = AttackEffect::EnergyReward(energy_regen); - let energy = EffectComponent::new(None, energy) - .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_physical()); - let damage = Damage { - source: DamageSource::Projectile, - value: damage, - }; - let damage = - DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)).with_effect(buff); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Projectile, + value: damage, + }, + Some(GroupTarget::OutOfGroup), + ) + .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.2) @@ -111,14 +108,15 @@ impl ProjectileConstructor { radius, energy_regen, } => { - let energy = AttackEffect::EnergyReward(energy_regen); - let energy = EffectComponent::new(None, energy) + let energy = AttackEffect::new(None, CombatEffect::EnergyReward(energy_regen)) .with_requirement(CombatRequirement::AnyDamage); - let damage = Damage { - source: DamageSource::Explosion, - value: damage, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Explosion, + value: damage, + }, + Some(GroupTarget::OutOfGroup), + ); let attack = Attack::default().with_damage(damage).with_effect(energy); let explosion = Explosion { effects: vec![ @@ -139,14 +137,15 @@ impl ProjectileConstructor { damage, energy_regen, } => { - let energy = AttackEffect::EnergyReward(energy_regen); - let energy = EffectComponent::new(None, energy) + let energy = AttackEffect::new(None, CombatEffect::EnergyReward(energy_regen)) .with_requirement(CombatRequirement::AnyDamage); - let damage = Damage { - source: DamageSource::Energy, - value: damage, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Energy, + value: damage, + }, + Some(GroupTarget::OutOfGroup), + ); let attack = Attack::default().with_damage(damage).with_effect(energy); Projectile { @@ -162,13 +161,14 @@ impl ProjectileConstructor { damage, radius, } => { - let damage = Damage { - source: DamageSource::Explosion, - value: damage, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); - let heal = AttackEffect::Heal(heal); - let heal = EffectComponent::new(Some(GroupTarget::InGroup), heal); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Explosion, + value: damage, + }, + Some(GroupTarget::OutOfGroup), + ); + let heal = AttackEffect::new(Some(GroupTarget::InGroup), CombatEffect::Heal(heal)); let attack = Attack::default().with_damage(damage).with_effect(heal); let explosion = Explosion { effects: vec![RadiusEffect::Attack(attack)], diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index ccaab77a04..f5b127fed7 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -1,7 +1,7 @@ use crate::{ combat::{ - Attack, AttackEffect, CombatRequirement, Damage, DamageComponent, DamageSource, - EffectComponent, GroupTarget, + Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement, Damage, DamageSource, + GroupTarget, }, comp::{beam, Body, CharacterState, EnergyChange, EnergySource, Ori, Pos, StateUpdate}, event::ServerEvent, @@ -122,25 +122,32 @@ impl CharacterBehavior for Data { if ability_key_is_pressed(data, self.static_data.ability_key) && (self.static_data.energy_drain == 0 || update.energy.current() > 0) { - let damage = Damage { - source: DamageSource::Energy, - value: self.static_data.base_dps as f32 / self.static_data.tick_rate, - }; - let heal = self.static_data.base_hps as f32 / self.static_data.tick_rate; - let heal = AttackEffect::Heal(heal); let speed = self.static_data.range / self.static_data.beam_duration.as_secs_f32(); - let energy = AttackEffect::EnergyReward(self.static_data.energy_regen); - let energy = EffectComponent::new(None, energy) - .with_requirement(CombatRequirement::AnyDamage); - let lifesteal = AttackEffect::Lifesteal(self.static_data.lifesteal_eff); - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(lifesteal); - let heal = EffectComponent::new(Some(GroupTarget::InGroup), heal) - .with_requirement(CombatRequirement::SufficientEnergy( - self.static_data.energy_cost, - )); + let energy = AttackEffect::new( + None, + CombatEffect::EnergyReward(self.static_data.energy_regen), + ) + .with_requirement(CombatRequirement::AnyDamage); + let lifesteal = CombatEffect::Lifesteal(self.static_data.lifesteal_eff); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Energy, + value: self.static_data.base_dps as f32 / self.static_data.tick_rate, + }, + Some(GroupTarget::OutOfGroup), + ) + .with_effect(lifesteal); + let heal = AttackEffect::new( + Some(GroupTarget::InGroup), + CombatEffect::Heal( + self.static_data.base_hps as f32 / self.static_data.tick_rate, + ), + ) + .with_requirement(CombatRequirement::SufficientEnergy( + self.static_data.energy_cost, + )); let attack = Attack::default() .with_damage(damage) .with_effect(energy) diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 778d624fbb..38c7a0972f 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -1,8 +1,6 @@ use crate::{ - combat::{ - Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, - }, - comp::{CharacterState, MeleeAttack, StateUpdate}, + combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement}, + comp::{CharacterState, Melee, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -92,25 +90,30 @@ impl CharacterBehavior for Data { ..*self }); - let poise = AttackEffect::Poise(self.static_data.base_poise_damage as f32); - let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) + let poise = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Poise(self.static_data.base_poise_damage as f32), + ) + .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(Knockback { + strength: self.static_data.knockback, + direction: KnockbackDir::Away, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); + let energy = AttackEffect::new(None, CombatEffect::EnergyReward(50)) .with_requirement(CombatRequirement::AnyDamage); - let knockback = AttackEffect::Knockback(Knockback { - strength: self.static_data.knockback, - direction: KnockbackDir::Away, - }); - let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) - .with_requirement(CombatRequirement::AnyDamage); - let energy = AttackEffect::EnergyReward(50); - let energy = EffectComponent::new(None, energy) - .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_physical()); - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(buff); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }, + Some(GroupTarget::OutOfGroup), + ) + .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) @@ -119,7 +122,7 @@ impl CharacterBehavior for Data { .with_effect(knockback); // Hit attempt - data.updater.insert(data.entity, MeleeAttack { + data.updater.insert(data.entity, Melee { attack, range: self.static_data.range, max_angle: self.static_data.max_angle, @@ -158,14 +161,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index c6c66df258..0e0a9e4917 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ Beam, Body, CharacterState, ControlAction, Controller, ControllerInputs, Energy, Health, - Inventory, MeleeAttack, Ori, PhysicsState, Pos, StateUpdate, Stats, Vel, + Inventory, Melee, Ori, PhysicsState, Pos, StateUpdate, Stats, Vel, }, resources::DeltaTime, uid::Uid, @@ -57,7 +57,7 @@ pub struct JoinData<'a> { pub inventory: &'a Inventory, pub body: &'a Body, pub physics: &'a PhysicsState, - pub melee_attack: Option<&'a MeleeAttack>, + pub melee_attack: Option<&'a Melee>, pub updater: &'a LazyUpdate, pub stats: &'a Stats, } @@ -84,7 +84,7 @@ pub type JoinTuple<'a> = ( &'a Health, &'a Body, &'a PhysicsState, - Option<&'a MeleeAttack>, + Option<&'a Melee>, Option<&'a Beam>, &'a Stats, ); diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index d8144b96b0..8ebc6749d6 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -1,8 +1,6 @@ use crate::{ - combat::{ - Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, - }, - comp::{CharacterState, EnergyChange, EnergySource, MeleeAttack, StateUpdate}, + combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement}, + comp::{CharacterState, EnergyChange, EnergySource, Melee, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, @@ -155,27 +153,33 @@ impl CharacterBehavior for Data { exhausted: true, ..*self }); - let poise = self.static_data.initial_poise_damage as f32 - + self.charge_amount * self.static_data.scaled_poise_damage as f32; - let poise = AttackEffect::Poise(poise); - let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) - .with_requirement(CombatRequirement::AnyDamage); - let knockback = self.static_data.initial_knockback - + self.charge_amount * self.static_data.scaled_knockback; - let knockback = AttackEffect::Knockback(Knockback { - strength: knockback, - direction: KnockbackDir::Away, - }); - let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) - .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_physical()); - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.initial_damage as f32 - + self.charge_amount * self.static_data.scaled_damage as f32, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(buff); + let poise = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Poise( + self.static_data.initial_poise_damage as f32 + + self.charge_amount * self.static_data.scaled_poise_damage as f32, + ), + ) + .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(Knockback { + strength: self.static_data.initial_knockback + + self.charge_amount * self.static_data.scaled_knockback, + direction: KnockbackDir::Away, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Melee, + value: self.static_data.initial_damage as f32 + + self.charge_amount * self.static_data.scaled_damage as f32, + }, + Some(GroupTarget::OutOfGroup), + ) + .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) @@ -183,7 +187,7 @@ impl CharacterBehavior for Data { .with_effect(knockback); // Hit attempt - data.updater.insert(data.entity, MeleeAttack { + data.updater.insert(data.entity, Melee { attack, range: self.static_data.range, max_angle: self.static_data.max_angle.to_radians(), @@ -222,14 +226,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 5759024143..9e6e16995b 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -1,7 +1,7 @@ use crate::{ combat::{ - Attack, AttackEffect, CombatBuff, CombatRequirement, Damage, DamageComponent, DamageSource, - EffectComponent, GroupTarget, Knockback, KnockbackDir, + Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement, Damage, + DamageSource, GroupTarget, Knockback, KnockbackDir, }, comp::{ projectile, Body, CharacterState, EnergyChange, EnergySource, Gravity, LightEmitter, @@ -103,22 +103,25 @@ impl CharacterBehavior for Data { let charge_frac = (self.timer.as_secs_f32() / self.static_data.charge_duration.as_secs_f32()) .min(1.0); - let knockback = self.static_data.initial_knockback - + charge_frac * self.static_data.scaled_knockback; - let knockback = AttackEffect::Knockback(Knockback { - strength: knockback, - direction: KnockbackDir::Away, - }); - let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) - .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_physical()); - let damage = Damage { - source: DamageSource::Projectile, - value: self.static_data.initial_damage as f32 - + charge_frac * self.static_data.scaled_damage as f32, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(buff); + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(Knockback { + strength: self.static_data.initial_knockback + + charge_frac * self.static_data.scaled_knockback, + direction: KnockbackDir::Away, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Projectile, + value: self.static_data.initial_damage as f32 + + charge_frac * self.static_data.scaled_damage as f32, + }, + Some(GroupTarget::OutOfGroup), + ) + .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.2) diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 3a24c54151..caffd8d35a 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -1,8 +1,6 @@ use crate::{ - combat::{ - Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, - }, - comp::{CharacterState, MeleeAttack, StateUpdate}, + combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement}, + comp::{CharacterState, Melee, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -174,37 +172,40 @@ impl CharacterBehavior for Data { .scales_from_combo .min(self.combo / self.static_data.num_stages) * self.static_data.stage_data[stage_index].damage_increase; - let poise = self.static_data.stage_data[stage_index].base_poise_damage + self .static_data .scales_from_combo .min(self.combo / self.static_data.num_stages) * self.static_data.stage_data[stage_index].poise_damage_increase; - let poise = AttackEffect::Poise(poise as f32); - let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) - .with_requirement(CombatRequirement::AnyDamage); - - let knockback = AttackEffect::Knockback(Knockback { - strength: self.static_data.stage_data[stage_index].knockback, - direction: KnockbackDir::Away, - }); - let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) - .with_requirement(CombatRequirement::AnyDamage); + let poise = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Poise(poise as f32), + ) + .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(Knockback { + strength: self.static_data.stage_data[stage_index].knockback, + direction: KnockbackDir::Away, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); let energy = self.static_data.max_energy_gain.min( self.static_data.initial_energy_gain + self.combo * self.static_data.energy_increase, ); - let energy = AttackEffect::EnergyReward(energy); - let energy = EffectComponent::new(None, energy) + let energy = AttackEffect::new(None, CombatEffect::EnergyReward(energy)) .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_physical()); - let damage = Damage { - source: DamageSource::Melee, - value: damage as f32, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(buff); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Melee, + value: damage as f32, + }, + Some(GroupTarget::OutOfGroup), + ) + .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) @@ -212,7 +213,7 @@ impl CharacterBehavior for Data { .with_effect(poise) .with_effect(knockback); - data.updater.insert(data.entity, MeleeAttack { + data.updater.insert(data.entity, Melee { attack, range: self.static_data.stage_data[stage_index].range, max_angle: self.static_data.stage_data[stage_index].angle.to_radians(), @@ -290,14 +291,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } @@ -312,7 +313,7 @@ impl CharacterBehavior for Data { stage_section: self.stage_section, next_stage: self.next_stage, }); - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } } diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 74191a1c42..c7875efe91 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -1,8 +1,6 @@ use crate::{ - combat::{ - Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, - }, - comp::{CharacterState, EnergyChange, EnergySource, MeleeAttack, StateUpdate}, + combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement}, + comp::{CharacterState, EnergyChange, EnergySource, Melee, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -132,36 +130,40 @@ impl CharacterBehavior for Data { if !self.exhausted { // Hit attempt (also checks if player is moving) if update.vel.0.distance_squared(Vec3::zero()) > 1.0 { - let poise = self.static_data.base_poise_damage as f32 - + charge_frac * self.static_data.scaled_poise_damage as f32; - let poise = AttackEffect::Poise(poise); - let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) - .with_requirement(CombatRequirement::AnyDamage); - let knockback = self.static_data.base_knockback - + charge_frac * self.static_data.scaled_knockback; - let knockback = AttackEffect::Knockback(Knockback { - strength: knockback, - direction: KnockbackDir::Away, - }); - let knockback = - EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) - .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_physical()); - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32 - + charge_frac * self.static_data.scaled_damage as f32, - }; - let damage = - DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(buff); + let poise = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Poise( + self.static_data.base_poise_damage as f32 + + charge_frac * self.static_data.scaled_poise_damage as f32, + ), + ) + .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(Knockback { + strength: self.static_data.base_knockback + + charge_frac * self.static_data.scaled_knockback, + direction: KnockbackDir::Away, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32 + + charge_frac * self.static_data.scaled_damage as f32, + }, + Some(GroupTarget::OutOfGroup), + ) + .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) .with_effect(poise) .with_effect(knockback); - data.updater.insert(data.entity, MeleeAttack { + data.updater.insert(data.entity, Melee { attack, range: self.static_data.range, max_angle: self.static_data.angle.to_radians(), @@ -246,14 +248,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 799bc37337..7c970282dd 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -1,8 +1,6 @@ use crate::{ - combat::{ - Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, - }, - comp::{CharacterState, MeleeAttack, StateUpdate}, + combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement}, + comp::{CharacterState, Melee, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, @@ -150,22 +148,28 @@ impl CharacterBehavior for Data { }, StageSection::Recover => { if !self.exhausted { - let poise = AttackEffect::Poise(self.static_data.base_poise_damage as f32); - let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) - .with_requirement(CombatRequirement::AnyDamage); - let knockback = AttackEffect::Knockback(Knockback { - strength: self.static_data.knockback, - direction: KnockbackDir::Away, - }); - let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) - .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_physical()); - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(buff); + let poise = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Poise(self.static_data.base_poise_damage as f32), + ) + .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(Knockback { + strength: self.static_data.knockback, + direction: KnockbackDir::Away, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }, + Some(GroupTarget::OutOfGroup), + ) + .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) @@ -173,7 +177,7 @@ impl CharacterBehavior for Data { .with_effect(knockback); // Hit attempt, when animation plays - data.updater.insert(data.entity, MeleeAttack { + data.updater.insert(data.entity, Melee { attack, range: self.static_data.range, max_angle: self.static_data.max_angle.to_radians(), @@ -202,14 +206,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index 2cb3ca6071..cb73d6aafa 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -1,7 +1,7 @@ use crate::{ combat::{ - Attack, AttackEffect, CombatRequirement, Damage, DamageComponent, DamageSource, - EffectComponent, GroupTarget, Knockback, + Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement, Damage, DamageSource, + GroupTarget, Knockback, }, comp::{shockwave, CharacterState, StateUpdate}, event::ServerEvent, @@ -83,17 +83,23 @@ impl CharacterBehavior for Data { }); } else { // Attack - let poise = AttackEffect::Poise(self.static_data.poise_damage as f32); - let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) - .with_requirement(CombatRequirement::AnyDamage); - let knockback = AttackEffect::Knockback(self.static_data.knockback); - let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) - .with_requirement(CombatRequirement::AnyDamage); - let damage = Damage { - source: DamageSource::Shockwave, - value: self.static_data.damage as f32, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)); + let poise = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Poise(self.static_data.poise_damage as f32), + ) + .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(self.static_data.knockback), + ) + .with_requirement(CombatRequirement::AnyDamage); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Shockwave, + value: self.static_data.damage as f32, + }, + Some(GroupTarget::OutOfGroup), + ); let attack = Attack::default() .with_damage(damage) .with_effect(poise) diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index ca0b1f6220..75ad25649c 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -1,8 +1,6 @@ use crate::{ - combat::{ - Attack, AttackEffect, CombatBuff, CombatRequirement, DamageComponent, EffectComponent, - }, - comp::{CharacterState, EnergyChange, EnergySource, MeleeAttack, StateUpdate}, + combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement}, + comp::{CharacterState, EnergyChange, EnergySource, Melee, StateUpdate}, consts::GRAVITY, states::{ behavior::{CharacterBehavior, JoinData}, @@ -115,22 +113,28 @@ impl CharacterBehavior for Data { ..*self }); - let poise = AttackEffect::Poise(self.static_data.base_poise_damage as f32); - let poise = EffectComponent::new(Some(GroupTarget::OutOfGroup), poise) - .with_requirement(CombatRequirement::AnyDamage); - let knockback = AttackEffect::Knockback(Knockback { - strength: self.static_data.knockback, - direction: KnockbackDir::Away, - }); - let knockback = EffectComponent::new(Some(GroupTarget::OutOfGroup), knockback) - .with_requirement(CombatRequirement::AnyDamage); - let buff = AttackEffect::Buff(CombatBuff::default_physical()); - let damage = Damage { - source: DamageSource::Melee, - value: self.static_data.base_damage as f32, - }; - let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup)) - .with_effect(buff); + let poise = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Poise(self.static_data.base_poise_damage as f32), + ) + .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::new( + Some(GroupTarget::OutOfGroup), + CombatEffect::Knockback(Knockback { + strength: self.static_data.knockback, + direction: KnockbackDir::Away, + }), + ) + .with_requirement(CombatRequirement::AnyDamage); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( + Damage { + source: DamageSource::Melee, + value: self.static_data.base_damage as f32, + }, + Some(GroupTarget::OutOfGroup), + ) + .with_effect(buff); let attack = Attack::default() .with_damage(damage) .with_crit(0.5, 1.3) @@ -138,7 +142,7 @@ impl CharacterBehavior for Data { .with_effect(knockback); // Hit attempt - data.updater.insert(data.entity, MeleeAttack { + data.updater.insert(data.entity, Melee { attack, range: self.static_data.range, max_angle: 180_f32.to_radians(), @@ -213,14 +217,14 @@ impl CharacterBehavior for Data { // Done update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section update.character = CharacterState::Wielding; // Make sure attack component is removed - data.updater.remove::(data.entity); + data.updater.remove::(data.entity); }, } diff --git a/common/sys/src/beam.rs b/common/sys/src/beam.rs index 5ebb3ccefa..aa8ec3d1ce 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -1,4 +1,5 @@ use common::{ + combat::AttackerInfo, comp::{ group, Beam, BeamSegment, Body, Energy, Health, HealthSource, Inventory, Last, Ori, Pos, Scale, @@ -168,25 +169,27 @@ impl<'a> System<'a> for Sys { continue; } - let server_events = beam_segment.properties.attack.apply_attack( + let attacker_info = + beam_owner + .zip(beam_segment.owner) + .map(|(entity, uid)| AttackerInfo { + entity, + uid, + energy: energies.get(entity), + }); + + beam_segment.properties.attack.apply_attack( target_group, - beam_owner, + attacker_info, b, inventory_b_maybe, - beam_segment.owner, - beam_owner.and_then(|e| energies.get(e)), ori.0, false, 1.0, + |e| server_emitter.emit(e), ); - if !server_events.is_empty() { - hit_entities.push(*uid_b); - } - - for event in server_events { - server_emitter.emit(event); - } + hit_entities.push(*uid_b); } } } diff --git a/common/sys/src/character_behavior.rs b/common/sys/src/character_behavior.rs index 8802ed7dba..b927d3483f 100644 --- a/common/sys/src/character_behavior.rs +++ b/common/sys/src/character_behavior.rs @@ -3,8 +3,8 @@ use specs::{Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, System, W use common::{ comp::{ inventory::slot::{EquipSlot, Slot}, - Beam, Body, CharacterState, Controller, Energy, Health, Inventory, MeleeAttack, Mounting, - Ori, PhysicsState, Poise, PoiseState, Pos, StateUpdate, Stats, Vel, + Beam, Body, CharacterState, Controller, Energy, Health, Inventory, Melee, Mounting, Ori, + PhysicsState, Poise, PoiseState, Pos, StateUpdate, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, @@ -69,7 +69,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Poise>, ReadStorage<'a, Body>, ReadStorage<'a, PhysicsState>, - ReadStorage<'a, MeleeAttack>, + ReadStorage<'a, Melee>, ReadStorage<'a, Beam>, ReadStorage<'a, Uid>, ReadStorage<'a, Mounting>, diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index b373ba5b70..5c5253362e 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -1,5 +1,6 @@ use common::{ - comp::{group, Body, CharacterState, Energy, Health, Inventory, MeleeAttack, Ori, Pos, Scale}, + combat::AttackerInfo, + comp::{group, Body, CharacterState, Energy, Health, Inventory, Melee, Ori, Pos, Scale}, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, span, @@ -29,7 +30,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Energy>, ReadStorage<'a, Inventory>, ReadStorage<'a, group::Group>, - WriteStorage<'a, MeleeAttack>, + WriteStorage<'a, Melee>, ReadStorage<'a, CharacterState>, ); @@ -129,25 +130,24 @@ impl<'a> System<'a> for Sys { let dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - let server_events = attack.attack.apply_attack( + let attacker_info = Some(AttackerInfo { + entity, + uid: *uid, + energy: energies.get(entity), + }); + + attack.attack.apply_attack( target_group, - Some(entity), + attacker_info, b, inventory_b_maybe, - Some(*uid), - energies.get(entity), dir, is_dodge, 1.0, + |e| server_emitter.emit(e), ); - if !server_events.is_empty() { - attack.hit_count += 1; - } - - for event in server_events { - server_emitter.emit(event); - } + attack.hit_count += 1; } } } diff --git a/common/sys/src/projectile.rs b/common/sys/src/projectile.rs index c80986deb2..67ff616534 100644 --- a/common/sys/src/projectile.rs +++ b/common/sys/src/projectile.rs @@ -1,4 +1,5 @@ use common::{ + combat::AttackerInfo, comp::{ projectile, Energy, Group, HealthSource, Inventory, Ori, PhysicsState, Pos, Projectile, Vel, }, @@ -109,50 +110,28 @@ impl<'a> System<'a> for Sys { let owner_entity = projectile .owner .and_then(|u| uid_allocator.retrieve_entity_internal(u.into())); - let server_events = attack.apply_attack( + + let attacker_info = + owner_entity.zip(projectile.owner).map(|(entity, uid)| { + AttackerInfo { + entity, + uid, + energy: energies.get(entity), + } + }); + + attack.apply_attack( target_group, - owner_entity, + attacker_info, target_entity, inventories.get(target_entity), - projectile.owner, - owner_entity.and_then(|e| energies.get(e)), ori.0, false, 1.0, + |e| server_emitter.emit(e), ); - - for event in server_events { - server_emitter.emit(event); - } } }, - // projectile::Effect::Knockback(knockback) => { - // if let Some(other_entity) = - // uid_allocator.retrieve_entity_internal(other.into()) - // { - // let impulse = knockback.calculate_impulse(ori.0); - // if !impulse.is_approx_zero() { - // server_emitter.emit(ServerEvent::Knockback { - // entity: other_entity, - // impulse, - // }); - // } - // } - // }, - // projectile::Effect::RewardEnergy(energy) => { - // if let Some(entity_owner) = projectile - // .owner - // .and_then(|u| uid_allocator.retrieve_entity_internal(u.into())) - // { - // server_emitter.emit(ServerEvent::EnergyChange { - // entity: entity_owner, - // change: EnergyChange { - // amount: energy as i32, - // source: EnergySource::HitEnemy, - // }, - // }); - // } - // }, projectile::Effect::Explode(e) => { server_emitter.emit(ServerEvent::Explosion { pos: pos.0, @@ -175,26 +154,6 @@ impl<'a> System<'a> for Sys { } } }, - // TODO: Change to effect after !1472 merges - // projectile::Effect::Buff { buff, chance } => { - // if let Some(entity) = - // uid_allocator.retrieve_entity_internal(other.into()) - // { - // if chance.map_or(true, |c| thread_rng().gen::() < c) { - // let source = if let Some(owner) = projectile.owner { - // BuffSource::Character { by: owner } - // } else { - // BuffSource::Unknown - // }; - // let buff = - // Buff::new(buff.kind, buff.data, buff.cat_ids, source); - // server_emitter.emit(ServerEvent::Buff { - // entity, - // buff_change: BuffChange::Add(buff), - // }); - // } - // } - // }, _ => {}, } } diff --git a/common/sys/src/shockwave.rs b/common/sys/src/shockwave.rs index f40be3f9d2..e5547a7761 100644 --- a/common/sys/src/shockwave.rs +++ b/common/sys/src/shockwave.rs @@ -1,4 +1,5 @@ use common::{ + combat::AttackerInfo, comp::{ group, Body, Energy, Health, HealthSource, Inventory, Last, Ori, PhysicsState, Pos, Scale, Shockwave, ShockwaveHitEntities, @@ -195,25 +196,27 @@ impl<'a> System<'a> for Sys { if hit { let dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - let server_events = shockwave.properties.attack.apply_attack( + let attacker_info = + shockwave_owner + .zip(shockwave.owner) + .map(|(entity, uid)| AttackerInfo { + entity, + uid, + energy: energies.get(entity), + }); + + shockwave.properties.attack.apply_attack( target_group, - shockwave_owner, + attacker_info, b, inventories.get(b), - shockwave.owner, - shockwave_owner.and_then(|e| energies.get(e)), dir, false, 1.0, + |e| server_emitter.emit(e), ); - if !server_events.is_empty() { - shockwave_hit_list.hit_entities.push(*uid_b); - } - - for event in server_events { - server_emitter.emit(event); - } + shockwave_hit_list.hit_entities.push(*uid_b); } } } diff --git a/common/sys/src/state.rs b/common/sys/src/state.rs index 9dea038ee0..136c4e7102 100644 --- a/common/sys/src/state.rs +++ b/common/sys/src/state.rs @@ -163,7 +163,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); - ecs.register::(); + ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 052c5be8e5..cdae5475eb 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -174,7 +174,7 @@ pub fn handle_beam(server: &mut Server, properties: beam::Properties, pos: Pos, heal: properties .attack .effects() - .any(|e| matches!(e.effect(), combat::AttackEffect::Heal(h) if *h > 0.0)), + .any(|e| matches!(e.effect(), combat::CombatEffect::Heal(h) if *h > 0.0)), }); state.create_beam(properties, pos, ori).build(); } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 3041ae58ea..eafae8edca 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -510,7 +510,8 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) source: DamageSource::Falling, value: falldmg, }; - let change = damage.modify_damage(inventories.get(entity), None, false, 0.0, 1.0); + let change = + damage.calculate_health_change(inventories.get(entity), None, false, 0.0, 1.0); health.change_by(change); } // Handle poise change @@ -578,7 +579,7 @@ pub fn handle_explosion( // Add an outcome // Uses radius as outcome power, makes negative if explosion has healing effect let outcome_power = explosion.radius - * if explosion.effects.iter().any(|e| matches!(e, RadiusEffect::Attack(a) if a.effects().any(|e| matches!(e.effect(), combat::AttackEffect::Heal(h) if *h > 0.0)))) { + * if explosion.effects.iter().any(|e| matches!(e, RadiusEffect::Attack(a) if a.effects().any(|e| matches!(e.effect(), combat::CombatEffect::Heal(h) if *h > 0.0)))) { -1.0 } else { 1.0 @@ -708,23 +709,27 @@ pub fn handle_explosion( .unwrap_or_else(Vec3::unit_z), ); - let server_events = attack.apply_attack( - target_group, - owner_entity, - entity_b, - inventory_b_maybe, - owner, - owner_entity.and_then(|e| energies.get(e)), - dir, - false, - strength, - ); + let attacker_info = + owner_entity + .zip(owner) + .map(|(entity, uid)| combat::AttackerInfo { + entity, + uid, + energy: energies.get(entity), + }); let server_eventbus = ecs.read_resource::>(); - for event in server_events { - server_eventbus.emit_now(event); - } + attack.apply_attack( + target_group, + attacker_info, + entity_b, + inventory_b_maybe, + dir, + false, + strength, + |e| server_eventbus.emit_now(e), + ); } } }, diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index af04924032..430d0ac0fd 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -90,7 +90,13 @@ impl StateExt for State { }, Effect::Damage(damage) => { let inventories = self.ecs().read_storage::(); - let change = damage.modify_damage(inventories.get(entity), source, false, 0.0, 1.0); + let change = damage.calculate_health_change( + inventories.get(entity), + source, + false, + 0.0, + 1.0, + ); self.ecs() .write_storage::() .get_mut(entity)