From 78879d5189f437fe9ff19280922b13858102a69a Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 27 Jan 2021 20:44:49 -0500 Subject: [PATCH] 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; - }*/ } } }