From 2690e9caa1ef89a8e97b4e174665c6b4391a3f0d Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 25 Jan 2021 21:50:16 -0500 Subject: [PATCH] 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; - } + }*/ } } }