From 63011241ea26680b9f570be53bff0c7839aff25a Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Fri, 16 Oct 2020 21:29:14 -0500 Subject: [PATCH 01/10] Added keyframes to all states that were lacking them. --- common/src/comp/ability.rs | 108 +++++---- common/src/comp/inventory/item/tool.rs | 124 +++++----- common/src/loadout_builder.rs | 10 +- common/src/states/basic_melee.rs | 165 ++++++++----- common/src/states/basic_ranged.rs | 160 ++++++------- common/src/states/boost.rs | 44 ++-- common/src/states/charged_ranged.rs | 309 ++++++++++++------------- common/src/states/equipping.rs | 34 ++- common/src/states/roll.rs | 115 +++++++-- common/src/states/utils.rs | 6 +- voxygen/src/hud/hotbar.rs | 2 +- voxygen/src/hud/skillbar.rs | 34 +-- voxygen/src/hud/slots.rs | 5 +- 13 files changed, 626 insertions(+), 490 deletions(-) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 04cf6e708f..32659deaee 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -59,16 +59,16 @@ pub enum CharacterAbility { BasicMelee { energy_cost: u32, buildup_duration: Duration, + swing_duration: Duration, recover_duration: Duration, - base_healthchange: i32, + base_damage: u32, knockback: f32, range: f32, max_angle: f32, }, BasicRanged { energy_cost: u32, - holdable: bool, - prepare_duration: Duration, + buildup_duration: Duration, recover_duration: Duration, projectile: Projectile, projectile_body: Body, @@ -91,7 +91,7 @@ pub enum CharacterAbility { reps_remaining: u32, }, Boost { - duration: Duration, + movement_duration: Duration, only_up: bool, }, DashMelee { @@ -169,7 +169,7 @@ pub enum CharacterAbility { max_damage: u32, initial_knockback: f32, max_knockback: f32, - prepare_duration: Duration, + buildup_duration: Duration, charge_duration: Duration, recover_duration: Duration, projectile_body: Body, @@ -356,24 +356,29 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { match ability { CharacterAbility::BasicMelee { buildup_duration, + swing_duration, recover_duration, - base_healthchange, + base_damage, knockback, range, max_angle, energy_cost: _, } => CharacterState::BasicMelee(basic_melee::Data { + static_data: basic_melee::StaticData { + buildup_duration: *buildup_duration, + swing_duration: *swing_duration, + recover_duration: *recover_duration, + base_damage: *base_damage, + knockback: *knockback, + range: *range, + max_angle: *max_angle, + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, exhausted: false, - buildup_duration: *buildup_duration, - recover_duration: *recover_duration, - base_healthchange: *base_healthchange, - knockback: *knockback, - range: *range, - max_angle: *max_angle, }), CharacterAbility::BasicRanged { - holdable, - prepare_duration, + buildup_duration, recover_duration, projectile, projectile_body, @@ -382,21 +387,29 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { projectile_speed, energy_cost: _, } => CharacterState::BasicRanged(basic_ranged::Data { + static_data: basic_ranged::StaticData { + buildup_duration: *buildup_duration, + recover_duration: *recover_duration, + projectile: projectile.clone(), + projectile_body: *projectile_body, + projectile_light: *projectile_light, + projectile_gravity: *projectile_gravity, + projectile_speed: *projectile_speed, + ability_key: key, + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, exhausted: false, - prepare_timer: Duration::default(), - holdable: *holdable, - prepare_duration: *prepare_duration, - recover_duration: *recover_duration, - projectile: projectile.clone(), - projectile_body: *projectile_body, - projectile_light: *projectile_light, - projectile_gravity: *projectile_gravity, - projectile_speed: *projectile_speed, - ability_key: key, }), - CharacterAbility::Boost { duration, only_up } => CharacterState::Boost(boost::Data { - duration: *duration, - only_up: *only_up, + CharacterAbility::Boost { + movement_duration, + only_up, + } => CharacterState::Boost(boost::Data { + static_data: boost::StaticData { + movement_duration: *movement_duration, + only_up: *only_up, + }, + timer: Duration::default(), }), CharacterAbility::DashMelee { energy_cost: _, @@ -438,7 +451,13 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { }), CharacterAbility::BasicBlock => CharacterState::BasicBlock, CharacterAbility::Roll => CharacterState::Roll(roll::Data { - remaining_duration: Duration::from_millis(500), + static_data: roll::StaticData { + buildup_duration: Duration::from_millis(100), + movement_duration: Duration::from_millis(300), + recover_duration: Duration::from_millis(100), + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, was_wielded: false, // false by default. utils might set it to true }), CharacterAbility::ComboMelee { @@ -566,7 +585,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { max_damage, initial_knockback, max_knockback, - prepare_duration, + buildup_duration, charge_duration, recover_duration, projectile_body, @@ -575,21 +594,24 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { initial_projectile_speed, max_projectile_speed, } => CharacterState::ChargedRanged(charged_ranged::Data { + static_data: charged_ranged::StaticData { + buildup_duration: *buildup_duration, + charge_duration: *charge_duration, + recover_duration: *recover_duration, + energy_drain: *energy_drain, + initial_damage: *initial_damage, + max_damage: *max_damage, + initial_knockback: *initial_knockback, + max_knockback: *max_knockback, + projectile_body: *projectile_body, + projectile_light: *projectile_light, + projectile_gravity: *projectile_gravity, + initial_projectile_speed: *initial_projectile_speed, + max_projectile_speed: *max_projectile_speed, + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, exhausted: false, - energy_drain: *energy_drain, - initial_damage: *initial_damage, - max_damage: *max_damage, - initial_knockback: *initial_knockback, - max_knockback: *max_knockback, - prepare_duration: *prepare_duration, - charge_duration: *charge_duration, - charge_timer: Duration::default(), - recover_duration: *recover_duration, - projectile_body: *projectile_body, - projectile_light: *projectile_light, - projectile_gravity: *projectile_gravity, - initial_projectile_speed: *initial_projectile_speed, - max_projectile_speed: *max_projectile_speed, }), CharacterAbility::RepeaterRanged { energy_cost: _, diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 9e847bce6d..4cc50a926d 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -206,9 +206,10 @@ impl Tool { Axe(_) => vec![ BasicMelee { energy_cost: 0, - buildup_duration: Duration::from_millis(700), + buildup_duration: Duration::from_millis(600), + swing_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(300), - base_healthchange: (-120.0 * self.base_power()) as i32, + base_damage: (120.0 * self.base_power()) as u32, knockback: 0.0, range: 3.5, max_angle: 20.0, @@ -244,9 +245,10 @@ impl Tool { Hammer(_) => vec![ BasicMelee { energy_cost: 0, - buildup_duration: Duration::from_millis(700), + buildup_duration: Duration::from_millis(600), + swing_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(300), - base_healthchange: (-120.0 * self.base_power()) as i32, + base_damage: (120.0 * self.base_power()) as u32, knockback: 0.0, range: 3.5, max_angle: 20.0, @@ -280,9 +282,10 @@ impl Tool { ], Farming(_) => vec![BasicMelee { energy_cost: 1, - buildup_duration: Duration::from_millis(700), + buildup_duration: Duration::from_millis(600), + swing_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(150), - base_healthchange: (-50.0 * self.base_power()) as i32, + base_damage: (50.0 * self.base_power()) as u32, knockback: 0.0, range: 3.5, max_angle: 20.0, @@ -290,8 +293,7 @@ impl Tool { Bow(_) => vec![ BasicRanged { energy_cost: 0, - holdable: true, - prepare_duration: Duration::from_millis(100), + buildup_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(400), projectile: Projectile { hit_solid: vec![projectile::Effect::Stick], @@ -317,7 +319,7 @@ impl Tool { max_damage: (200.0 * self.base_power()) as u32, initial_knockback: 10.0, max_knockback: 20.0, - prepare_duration: Duration::from_millis(100), + buildup_duration: Duration::from_millis(100), charge_duration: Duration::from_millis(1500), recover_duration: Duration::from_millis(500), projectile_body: Body::Object(object::Body::MultiArrow), @@ -355,8 +357,9 @@ impl Tool { Dagger(_) => vec![BasicMelee { energy_cost: 0, buildup_duration: Duration::from_millis(100), - recover_duration: Duration::from_millis(400), - base_healthchange: (-50.0 * self.base_power()) as i32, + swing_duration: Duration::from_millis(100), + recover_duration: Duration::from_millis(300), + base_damage: (50.0 * self.base_power()) as u32, knockback: 0.0, range: 3.5, max_angle: 20.0, @@ -378,8 +381,7 @@ impl Tool { }, BasicRanged { energy_cost: 800, - holdable: true, - prepare_duration: Duration::from_millis(800), + buildup_duration: Duration::from_millis(800), recover_duration: Duration::from_millis(50), projectile: Projectile { hit_solid: vec![ @@ -422,8 +424,7 @@ impl Tool { Staff(_) => vec![ BasicRanged { energy_cost: 0, - holdable: false, - prepare_duration: Duration::from_millis(500), + buildup_duration: Duration::from_millis(500), recover_duration: Duration::from_millis(350), projectile: Projectile { hit_solid: vec![ @@ -495,8 +496,9 @@ impl Tool { BasicMelee { energy_cost: 0, buildup_duration: Duration::from_millis(100), - recover_duration: Duration::from_millis(400), - base_healthchange: (-40.0 * self.base_power()) as i32, + swing_duration: Duration::from_millis(100), + recover_duration: Duration::from_millis(300), + base_damage: (40.0 * self.base_power()) as u32, knockback: 0.0, range: 3.0, max_angle: 120.0, @@ -508,10 +510,11 @@ impl Tool { vec![ BasicMelee { energy_cost: 0, - buildup_duration: Duration::from_millis(500), + buildup_duration: Duration::from_millis(400), + swing_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(250), knockback: 25.0, - base_healthchange: -200, + base_damage: 200, range: 5.0, max_angle: 120.0, }, @@ -533,10 +536,11 @@ impl Tool { } else if kind == "BeastClaws" { vec![BasicMelee { energy_cost: 0, - buildup_duration: Duration::from_millis(500), + buildup_duration: Duration::from_millis(250), + swing_duration: Duration::from_millis(250), recover_duration: Duration::from_millis(250), knockback: 25.0, - base_healthchange: -200, + base_damage: 200, range: 5.0, max_angle: 120.0, }] @@ -544,58 +548,50 @@ impl Tool { vec![BasicMelee { energy_cost: 0, buildup_duration: Duration::from_millis(100), - recover_duration: Duration::from_millis(300), - base_healthchange: -10, + swing_duration: Duration::from_millis(100), + recover_duration: Duration::from_millis(200), + base_damage: 10, knockback: 0.0, range: 1.0, max_angle: 30.0, }] } }, - Debug(kind) => { - if kind == "Boost" { - vec![ - CharacterAbility::Boost { - duration: Duration::from_millis(50), - only_up: false, - }, - CharacterAbility::Boost { - duration: Duration::from_millis(50), - only_up: true, - }, - BasicRanged { - energy_cost: 0, - holdable: false, - prepare_duration: Duration::from_millis(0), - recover_duration: Duration::from_millis(10), - projectile: Projectile { - hit_solid: vec![projectile::Effect::Stick], - hit_entity: vec![ - projectile::Effect::Stick, - projectile::Effect::Possess, - ], - time_left: Duration::from_secs(10), - owner: None, - ignore_group: false, - }, - projectile_body: Body::Object(object::Body::ArrowSnake), - projectile_light: Some(LightEmitter { - col: (0.0, 1.0, 0.33).into(), - ..Default::default() - }), - projectile_gravity: None, - projectile_speed: 100.0, - }, - ] - } else { - vec![] - } - }, + Debug(_) => vec![ + CharacterAbility::Boost { + movement_duration: Duration::from_millis(50), + only_up: false, + }, + CharacterAbility::Boost { + movement_duration: Duration::from_millis(50), + only_up: true, + }, + BasicRanged { + energy_cost: 0, + buildup_duration: Duration::from_millis(0), + recover_duration: Duration::from_millis(10), + projectile: Projectile { + hit_solid: vec![projectile::Effect::Stick], + hit_entity: vec![projectile::Effect::Stick, projectile::Effect::Possess], + time_left: Duration::from_secs(10), + owner: None, + ignore_group: false, + }, + projectile_body: Body::Object(object::Body::ArrowSnake), + projectile_light: Some(LightEmitter { + col: (0.0, 1.0, 0.33).into(), + ..Default::default() + }), + projectile_gravity: None, + projectile_speed: 100.0, + }, + ], Empty => vec![BasicMelee { energy_cost: 0, buildup_duration: Duration::from_millis(0), - recover_duration: Duration::from_millis(1000), - base_healthchange: -20, + swing_duration: Duration::from_millis(100), + recover_duration: Duration::from_millis(900), + base_damage: 20, knockback: 0.0, range: 3.5, max_angle: 15.0, diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index 3fcb1c2fbb..5511a7826d 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -163,8 +163,9 @@ impl LoadoutBuilder { ability1: Some(CharacterAbility::BasicMelee { energy_cost: 0, buildup_duration: Duration::from_millis(0), - recover_duration: Duration::from_millis(400), - base_healthchange: -40, + swing_duration: Duration::from_millis(100), + recover_duration: Duration::from_millis(300), + base_damage: 40, knockback: 0.0, range: 3.5, max_angle: 15.0, @@ -343,9 +344,10 @@ impl LoadoutBuilder { item: Item::new_from_asset_expect("common.items.weapons.empty.empty"), ability1: Some(CharacterAbility::BasicMelee { energy_cost: 10, - buildup_duration: Duration::from_millis(600), + buildup_duration: Duration::from_millis(500), + swing_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(100), - base_healthchange: -(body.base_dmg() as i32), + base_damage: body.base_dmg(), knockback: 0.0, range: body.base_range(), max_angle: 20.0, diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 0d98e0aeac..dc3f6a4041 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -6,20 +6,34 @@ use crate::{ use serde::{Deserialize, Serialize}; use std::time::Duration; +/// Separated out to condense update portions of character state #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Data { +pub struct StaticData { /// How long until state should deal damage pub buildup_duration: Duration, + /// How long the state is swinging for + pub swing_duration: Duration, /// How long the state has until exiting pub recover_duration: Duration, - /// Base damage (negative) or healing (positive) - pub base_healthchange: i32, + /// Base damage + pub base_damage: u32, /// Knockback pub knockback: f32, /// Max range pub range: f32, /// Max angle (45.0 will give you a 90.0 angle window) pub max_angle: f32, +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, + /// What section the character stage is in + pub stage_section: StageSection, /// Whether the attack can deal more damage pub exhausted: bool, } @@ -31,65 +45,94 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 0.7); handle_jump(data, &mut update); - if self.buildup_duration != Duration::default() { - // Build up - update.character = CharacterState::BasicMelee(Data { - buildup_duration: self - .buildup_duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - recover_duration: self.recover_duration, - base_healthchange: self.base_healthchange, - knockback: self.knockback, - range: self.range, - max_angle: self.max_angle, - exhausted: false, - }); - } else if !self.exhausted { - let (damage, heal) = if self.base_healthchange > 0 { - (0, self.base_healthchange as u32) - } else { - ((-self.base_healthchange) as u32, 0) - }; - // Hit attempt - data.updater.insert(data.entity, Attacking { - base_damage: damage, - base_heal: heal, - range: self.range, - max_angle: self.max_angle.to_radians(), - applied: false, - hit_count: 0, - knockback: self.knockback, - }); + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::BasicMelee(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }); + } else { + // Transitions to swing section of stage + update.character = CharacterState::BasicMelee(Data { + static_data: self.static_data, + timer: Duration::default(), + stage_section: StageSection::Swing, + exhausted: self.exhausted, + }); + } + }, + StageSection::Swing => { + if !self.exhausted { + update.character = CharacterState::BasicMelee(Data { + static_data: self.static_data, + timer: Duration::default(), + stage_section: self.stage_section, + exhausted: true, + }); - update.character = CharacterState::BasicMelee(Data { - buildup_duration: self.buildup_duration, - recover_duration: self.recover_duration, - base_healthchange: self.base_healthchange, - knockback: self.knockback, - range: self.range, - max_angle: self.max_angle, - exhausted: true, - }); - } else if self.recover_duration != Duration::default() { - // Recovery - update.character = CharacterState::BasicMelee(Data { - buildup_duration: self.buildup_duration, - recover_duration: self - .recover_duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - base_healthchange: self.base_healthchange, - knockback: self.knockback, - range: self.range, - max_angle: self.max_angle, - exhausted: true, - }); - } else { - // Done - update.character = CharacterState::Wielding; - // Make sure attack component is removed - data.updater.remove::<Attacking>(data.entity); + // Hit attempt + data.updater.insert(data.entity, Attacking { + base_damage: self.static_data.base_damage, + base_heal: 0, + range: self.static_data.range, + max_angle: 180_f32.to_radians(), + applied: false, + hit_count: 0, + knockback: self.static_data.knockback, + }); + } else if self.timer < self.static_data.swing_duration { + // Swings + update.character = CharacterState::BasicMelee(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }); + } else { + // Transitions to recover section of stage + update.character = CharacterState::BasicMelee(Data { + static_data: self.static_data, + timer: Duration::default(), + stage_section: StageSection::Recover, + exhausted: self.exhausted, + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + // Recovery + update.character = CharacterState::BasicMelee(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }); + } else { + // Done + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::<Attacking>(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::<Attacking>(data.entity); + }, } // Grant energy on successful hit diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index cbfc6afc7a..bb0950e605 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -7,27 +7,36 @@ use crate::{ use serde::{Deserialize, Serialize}; use std::time::Duration; +/// Separated out to condense update portions of character state #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Data { - /// Can you hold the ability beyond the prepare duration - pub holdable: bool, - /// How long we have to prepare the weapon - pub prepare_duration: Duration, - /// How long we prepared the weapon already - pub prepare_timer: Duration, +pub struct StaticData { + /// How much buildup is required before the attack + pub buildup_duration: Duration, /// How long the state has until exiting pub recover_duration: Duration, + /// Projectile variables pub projectile: Projectile, pub projectile_body: Body, pub projectile_light: Option<LightEmitter>, pub projectile_gravity: Option<Gravity>, pub projectile_speed: f32, - /// Whether the attack fired already - pub exhausted: bool, /// What key is used to press ability pub ability_key: AbilityKey, } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, + /// What section the character stage is in + pub stage_section: StageSection, + /// Whether the attack fired already + pub exhausted: bool, +} + impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); @@ -35,77 +44,70 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 0.3); handle_jump(data, &mut update); - if !self.exhausted - && if self.holdable { - ability_key_is_pressed(data, self.ability_key) - || self.prepare_timer < self.prepare_duration - } else { - self.prepare_timer < self.prepare_duration - } - { - // Prepare (draw the bow) - update.character = CharacterState::BasicRanged(Data { - prepare_timer: self.prepare_timer + Duration::from_secs_f32(data.dt.0), - holdable: self.holdable, - prepare_duration: self.prepare_duration, - recover_duration: self.recover_duration, - projectile: self.projectile.clone(), - projectile_body: self.projectile_body, - projectile_light: self.projectile_light, - projectile_gravity: self.projectile_gravity, - projectile_speed: self.projectile_speed, - exhausted: false, - ability_key: self.ability_key, - }); - } else if !self.exhausted { - // Fire - let mut projectile = self.projectile.clone(); - projectile.owner = Some(*data.uid); - update.server_events.push_front(ServerEvent::Shoot { - entity: data.entity, - dir: data.inputs.look_dir, - body: self.projectile_body, - projectile, - light: self.projectile_light, - gravity: self.projectile_gravity, - speed: self.projectile_speed, - }); + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::BasicRanged(Data { + static_data: self.static_data.clone(), + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }); + } else { + // Transitions to recover section of stage + update.character = CharacterState::BasicRanged(Data { + static_data: self.static_data.clone(), + timer: Duration::default(), + stage_section: StageSection::Recover, + exhausted: self.exhausted, + }); + } + }, + StageSection::Recover => { + if !self.exhausted { + // Fire + let mut projectile = self.static_data.projectile.clone(); + projectile.owner = Some(*data.uid); + update.server_events.push_front(ServerEvent::Shoot { + entity: data.entity, + dir: data.inputs.look_dir, + body: self.static_data.projectile_body, + projectile, + light: self.static_data.projectile_light, + gravity: self.static_data.projectile_gravity, + speed: self.static_data.projectile_speed, + }); - update.character = CharacterState::BasicRanged(Data { - prepare_timer: self.prepare_timer, - holdable: self.holdable, - prepare_duration: self.prepare_duration, - recover_duration: self.recover_duration, - projectile: self.projectile.clone(), - projectile_body: self.projectile_body, - projectile_light: self.projectile_light, - projectile_gravity: self.projectile_gravity, - projectile_speed: self.projectile_speed, - exhausted: true, - ability_key: self.ability_key, - }); - } else if self.recover_duration != Duration::default() { - // Recovery - update.character = CharacterState::BasicRanged(Data { - prepare_timer: self.prepare_timer, - holdable: self.holdable, - prepare_duration: self.prepare_duration, - recover_duration: self - .recover_duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - projectile: self.projectile.clone(), - projectile_body: self.projectile_body, - projectile_light: self.projectile_light, - projectile_gravity: self.projectile_gravity, - projectile_speed: self.projectile_speed, - exhausted: true, - ability_key: self.ability_key, - }); - return update; - } else { - // Done - update.character = CharacterState::Wielding; + update.character = CharacterState::BasicRanged(Data { + static_data: self.static_data.clone(), + timer: self.timer, + stage_section: self.stage_section, + exhausted: true, + }); + } else if self.timer < self.static_data.recover_duration { + // Recovers + update.character = CharacterState::BasicRanged(Data { + static_data: self.static_data.clone(), + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }); + } else { + // Done + update.character = CharacterState::Wielding; + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Wielding; + }, } update diff --git a/common/src/states/boost.rs b/common/src/states/boost.rs index 52f1994152..564a714c57 100644 --- a/common/src/states/boost.rs +++ b/common/src/states/boost.rs @@ -1,16 +1,25 @@ use crate::{ - comp::{Attacking, CharacterState, EnergySource, StateUpdate}, + comp::{CharacterState, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, }; use serde::{Deserialize, Serialize}; use std::time::Duration; +/// Separated out to condense update portions of character state +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + pub movement_duration: Duration, + pub only_up: bool, +} + #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Data { - /// How long the state has until exiting - pub duration: Duration, - pub only_up: bool, + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, } impl CharacterBehavior for Data { @@ -19,34 +28,25 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 1.0); - // Still going - if self.duration != Duration::default() { - if self.only_up { + if self.timer < self.static_data.movement_duration { + // Movement + if self.static_data.only_up { update.vel.0.z += 500.0 * data.dt.0; } else { update.vel.0 += *data.inputs.look_dir * 500.0 * data.dt.0; } update.character = CharacterState::Boost(Data { - duration: self - .duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - only_up: self.only_up, }); - } - // Done - else { + } else { + // Done update.character = CharacterState::Wielding; } - // Grant energy on successful hit - if let Some(attack) = data.attacking { - if attack.applied && attack.hit_count > 0 { - data.updater.remove::<Attacking>(data.entity); - update.energy.change_by(100, EnergySource::HitEnemy); - } - } - update } } diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 1386b8b4d5..5413eaa386 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -10,10 +10,15 @@ use crate::{ use serde::{Deserialize, Serialize}; use std::time::Duration; -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Data { - /// Whether the attack fired already - pub exhausted: bool, +/// Separated out to condense update portions of character state +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + /// How long the weapon needs to be prepared for + pub buildup_duration: Duration, + /// How long it takes to charge the weapon to max damage and knockback + pub charge_duration: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, /// How much energy is drained per second when charging pub energy_drain: u32, /// How much damage is dealt with no charge @@ -24,14 +29,6 @@ pub struct Data { pub initial_knockback: f32, /// How much knockback there is at max charge pub max_knockback: f32, - /// How long the weapon needs to be prepared for - pub prepare_duration: Duration, - /// How long it takes to charge the weapon to max damage and knockback - pub charge_duration: Duration, - /// How long the state has been charging - pub charge_timer: Duration, - /// How long the state has until exiting - pub recover_duration: Duration, /// Projectile information pub projectile_body: Body, pub projectile_light: Option<LightEmitter>, @@ -40,6 +37,19 @@ pub struct Data { pub max_projectile_speed: f32, } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, + /// What section the character stage is in + pub stage_section: StageSection, + /// Whether the attack fired already + pub exhausted: bool, +} + impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); @@ -47,160 +57,133 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 0.3); handle_jump(data, &mut update); - if self.prepare_duration != Duration::default() { - // Prepare (draw the bow) - update.character = CharacterState::ChargedRanged(Data { - exhausted: self.exhausted, - energy_drain: self.energy_drain, - initial_damage: self.initial_damage, - max_damage: self.max_damage, - initial_knockback: self.initial_knockback, - max_knockback: self.max_knockback, - prepare_duration: self - .prepare_duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - charge_duration: self.charge_duration, - charge_timer: self.charge_timer, - recover_duration: self.recover_duration, - projectile_body: self.projectile_body, - projectile_light: self.projectile_light, - projectile_gravity: self.projectile_gravity, - initial_projectile_speed: self.initial_projectile_speed, - max_projectile_speed: self.max_projectile_speed, - }); - } else if data.inputs.secondary.is_pressed() - && self.charge_timer < self.charge_duration - && update.energy.current() > 0 - { - // Charge the bow - update.character = CharacterState::ChargedRanged(Data { - exhausted: self.exhausted, - energy_drain: self.energy_drain, - initial_damage: self.initial_damage, - max_damage: self.max_damage, - initial_knockback: self.initial_knockback, - max_knockback: self.max_knockback, - prepare_duration: self.prepare_duration, - charge_timer: self - .charge_timer - .checked_add(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - charge_duration: self.charge_duration, - recover_duration: self.recover_duration, - projectile_body: self.projectile_body, - projectile_light: self.projectile_light, - projectile_gravity: self.projectile_gravity, - initial_projectile_speed: self.initial_projectile_speed, - max_projectile_speed: self.max_projectile_speed, - }); + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::ChargedRanged(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }); + } else { + // Transitions to swing section of stage + update.character = CharacterState::ChargedRanged(Data { + static_data: self.static_data, + timer: Duration::default(), + stage_section: StageSection::Charge, + exhausted: self.exhausted, + }); + } + }, + StageSection::Charge => { + if !data.inputs.secondary.is_pressed() && !self.exhausted { + let charge_frac = (self.timer.as_secs_f32() + / self.static_data.charge_duration.as_secs_f32()) + .min(1.0); + let damage = self.static_data.initial_damage as f32 + + (charge_frac + * (self.static_data.max_damage - self.static_data.initial_damage) + as f32); + let knockback = self.static_data.initial_knockback as f32 + + (charge_frac + * (self.static_data.max_knockback - self.static_data.initial_knockback) + as f32); + // Fire + let mut projectile = Projectile { + hit_solid: vec![projectile::Effect::Stick], + hit_entity: vec![ + projectile::Effect::Damage(-damage as i32), + projectile::Effect::Knockback(knockback), + projectile::Effect::Vanish, + ], + time_left: Duration::from_secs(15), + owner: None, + ignore_group: true, + }; + projectile.owner = Some(*data.uid); + update.server_events.push_front(ServerEvent::Shoot { + entity: data.entity, + dir: data.inputs.look_dir, + body: self.static_data.projectile_body, + projectile, + light: self.static_data.projectile_light, + gravity: self.static_data.projectile_gravity, + speed: self.static_data.initial_projectile_speed + + charge_frac + * (self.static_data.max_projectile_speed + - self.static_data.initial_projectile_speed), + }); - // Consumes energy if there's enough left and RMB is held down - update.energy.change_by( - -(self.energy_drain as f32 * data.dt.0) as i32, - EnergySource::Ability, - ); - } else if data.inputs.secondary.is_pressed() { - // Charge the bow - update.character = CharacterState::ChargedRanged(Data { - exhausted: self.exhausted, - energy_drain: self.energy_drain, - initial_damage: self.initial_damage, - max_damage: self.max_damage, - initial_knockback: self.initial_knockback, - max_knockback: self.max_knockback, - prepare_duration: self.prepare_duration, - charge_timer: self.charge_timer, - charge_duration: self.charge_duration, - recover_duration: self.recover_duration, - projectile_body: self.projectile_body, - projectile_light: self.projectile_light, - projectile_gravity: self.projectile_gravity, - initial_projectile_speed: self.initial_projectile_speed, - max_projectile_speed: self.max_projectile_speed, - }); + update.character = CharacterState::ChargedRanged(Data { + static_data: self.static_data, + timer: Duration::default(), + stage_section: StageSection::Recover, + exhausted: true, + }); + } else if self.timer < self.static_data.charge_duration + && data.inputs.secondary.is_pressed() + { + // Charges + update.character = CharacterState::ChargedRanged(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }); - // Consumes energy if there's enough left and RMB is held down - update.energy.change_by( - -(self.energy_drain as f32 * data.dt.0 / 5.0) as i32, - EnergySource::Ability, - ); - } else if !self.exhausted { - let charge_amount = - (self.charge_timer.as_secs_f32() / self.charge_duration.as_secs_f32()).min(1.0); - // Fire - let mut projectile = Projectile { - hit_solid: vec![projectile::Effect::Stick], - hit_entity: vec![ - projectile::Effect::Damage( - -(self.initial_damage as i32 - + (charge_amount * (self.max_damage - self.initial_damage) as f32) - as i32), - ), - projectile::Effect::Knockback( - self.initial_knockback - + charge_amount * (self.max_knockback - self.initial_knockback), - ), - projectile::Effect::Vanish, - ], - time_left: Duration::from_secs(15), - owner: None, - ignore_group: true, - }; - projectile.owner = Some(*data.uid); - update.server_events.push_front(ServerEvent::Shoot { - entity: data.entity, - dir: data.inputs.look_dir, - body: self.projectile_body, - projectile, - light: self.projectile_light, - gravity: self.projectile_gravity, - speed: self.initial_projectile_speed - + charge_amount * (self.max_projectile_speed - self.initial_projectile_speed), - }); + // Consumes energy if there's enough left and RMB is held down + update.energy.change_by( + -(self.static_data.energy_drain as f32 * data.dt.0) as i32, + EnergySource::Ability, + ); + } else if data.inputs.secondary.is_pressed() { + // Holds charge + update.character = CharacterState::ChargedRanged(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }); - update.character = CharacterState::ChargedRanged(Data { - exhausted: true, - energy_drain: self.energy_drain, - initial_damage: self.initial_damage, - max_damage: self.max_damage, - initial_knockback: self.initial_knockback, - max_knockback: self.max_knockback, - prepare_duration: self.prepare_duration, - charge_timer: self.charge_timer, - charge_duration: self.charge_duration, - recover_duration: self.recover_duration, - projectile_body: self.projectile_body, - projectile_light: self.projectile_light, - projectile_gravity: self.projectile_gravity, - initial_projectile_speed: self.initial_projectile_speed, - max_projectile_speed: self.max_projectile_speed, - }); - } else if self.recover_duration != Duration::default() { - // Recovery - update.character = CharacterState::ChargedRanged(Data { - exhausted: self.exhausted, - energy_drain: self.energy_drain, - initial_damage: self.initial_damage, - max_damage: self.max_damage, - initial_knockback: self.initial_knockback, - max_knockback: self.max_knockback, - prepare_duration: self.prepare_duration, - charge_timer: self.charge_timer, - charge_duration: self.charge_duration, - recover_duration: self - .recover_duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - projectile_body: self.projectile_body, - projectile_light: self.projectile_light, - projectile_gravity: self.projectile_gravity, - initial_projectile_speed: self.initial_projectile_speed, - max_projectile_speed: self.max_projectile_speed, - }); - } else { - // Done - update.character = CharacterState::Wielding; + // Consumes energy if there's enough left and RMB is held down + update.energy.change_by( + -(self.static_data.energy_drain as f32 * data.dt.0 / 5.0) as i32, + EnergySource::Ability, + ); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + // Recovers + update.character = CharacterState::ChargedRanged(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }); + } else { + // Done + update.character = CharacterState::Wielding; + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Wielding; + }, } update diff --git a/common/src/states/equipping.rs b/common/src/states/equipping.rs index ebc91062ff..b0e63c11ea 100644 --- a/common/src/states/equipping.rs +++ b/common/src/states/equipping.rs @@ -6,10 +6,20 @@ use crate::{ use serde::{Deserialize, Serialize}; use std::time::Duration; -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +/// Separated out to condense update portions of character state +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + /// Time required to draw weapon + pub buildup_duration: Duration, +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Data { - /// Time left before next state - pub time_left: Duration, + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, } impl CharacterBehavior for Data { @@ -19,18 +29,18 @@ impl CharacterBehavior for Data { handle_move(&data, &mut update, 1.0); handle_jump(&data, &mut update); - if self.time_left == Duration::default() { - // Wield delay has expired - update.character = CharacterState::Wielding; - } else { - // Wield delay hasn't expired yet - // Update wield delay + if self.timer < self.static_data.buildup_duration { + // Draw weapon update.character = CharacterState::Equipping(Data { - time_left: self - .time_left - .checked_sub(Duration::from_secs_f32(data.dt.0)) + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), }); + } else { + // Done + update.character = CharacterState::Wielding; } update diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index 3dc7887e7f..2a49f44445 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -1,5 +1,6 @@ use crate::{ comp::{CharacterState, StateUpdate}, + states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, util::Dir, }; @@ -8,10 +9,27 @@ use std::time::Duration; use vek::Vec3; const ROLL_SPEED: f32 = 25.0; -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] + +/// Separated out to condense update portions of character state +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + /// How long until state should roll + pub buildup_duration: Duration, + /// How long state is rolling for + pub movement_duration: Duration, + /// How long it takes to recover from roll + pub recover_duration: Duration, +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Data { - /// How long the state has until exiting - pub remaining_duration: Duration, + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, + /// What section the character stage is in + pub stage_section: StageSection, /// Had weapon pub was_wielded: bool, } @@ -31,23 +49,80 @@ impl CharacterBehavior for Data { // Smooth orientation update.ori.0 = Dir::slerp_to_vec3(update.ori.0, update.vel.0.xy().into(), 9.0 * data.dt.0); - if self.remaining_duration == Duration::default() { - // Roll duration has expired - update.vel.0 *= 0.3; - if self.was_wielded { - update.character = CharacterState::Wielding; - } else { - update.character = CharacterState::Idle; - } - } else { - // Otherwise, tick down remaining_duration - update.character = CharacterState::Roll(Data { - remaining_duration: self - .remaining_duration - .checked_sub(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - was_wielded: self.was_wielded, - }); + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::Roll(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + was_wielded: self.was_wielded, + }); + } else { + // Transitions to movement section of stage + update.character = CharacterState::Roll(Data { + static_data: self.static_data, + timer: Duration::default(), + stage_section: StageSection::Movement, + was_wielded: self.was_wielded, + }); + } + }, + StageSection::Movement => { + if self.timer < self.static_data.movement_duration { + // Movement + update.character = CharacterState::Roll(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + was_wielded: self.was_wielded, + }); + } else { + // Transitions to recover section of stage + update.character = CharacterState::Roll(Data { + static_data: self.static_data, + timer: Duration::default(), + stage_section: StageSection::Recover, + was_wielded: self.was_wielded, + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + // Build up + update.character = CharacterState::Roll(Data { + static_data: self.static_data, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + was_wielded: self.was_wielded, + }); + } else { + // Done + if self.was_wielded { + update.character = CharacterState::Wielding; + } else { + update.character = CharacterState::Idle; + } + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + if self.was_wielded { + update.character = CharacterState::Wielding; + } else { + update.character = CharacterState::Idle; + } + }, } update diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index c0b71b7b5c..13e0f50269 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -9,6 +9,7 @@ use crate::{ util::Dir, }; use serde::{Deserialize, Serialize}; +use std::time::Duration; use vek::*; pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; @@ -161,7 +162,10 @@ pub fn handle_wield(data: &JoinData, update: &mut StateUpdate) { pub fn attempt_wield(data: &JoinData, update: &mut StateUpdate) { if let Some(ItemKind::Tool(tool)) = data.loadout.active_item.as_ref().map(|i| i.item.kind()) { update.character = CharacterState::Equipping(equipping::Data { - time_left: tool.equip_time(), + static_data: equipping::StaticData { + buildup_duration: tool.equip_time(), + }, + timer: Duration::default(), }); } else { update.character = CharacterState::Idle; diff --git a/voxygen/src/hud/hotbar.rs b/voxygen/src/hud/hotbar.rs index e17245c2a8..7be45e7de9 100644 --- a/voxygen/src/hud/hotbar.rs +++ b/voxygen/src/hud/hotbar.rs @@ -79,7 +79,7 @@ impl State { if let ItemKind::Tool(kind) = kind { match &kind.kind { ToolKind::Staff(_) => true, - ToolKind::Debug(kind) => kind == "Boost", + ToolKind::Debug(_) => true, ToolKind::Sword(_) => true, ToolKind::Hammer(_) => true, ToolKind::Axe(_) => true, diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 172671622e..ad8045517d 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -524,6 +524,14 @@ impl<'a> Widget for Skillbar<'a> { .map(|i| i.item.kind()) .and_then(|kind| match kind { ItemKind::Tool(Tool { kind, .. }) => match kind { + ToolKind::Hammer(_) => Some(( + "Smash of Doom", + "\nAn AOE attack with knockback. \nLeaps to position of \ + cursor.", + )), + ToolKind::Axe(_) => { + Some(("Spin Leap", "\nA slashing running spin leap.")) + }, ToolKind::Staff(_) => Some(( "Firebomb", "\nWhirls a big fireball into the air. \nExplodes the ground \ @@ -533,14 +541,14 @@ impl<'a> Widget for Skillbar<'a> { "Whirlwind", "\nMove forward while spinning with \n your sword.", )), - ToolKind::Debug(kind) => match kind.as_ref() { - "Boost" => Some(( - "Possessing Arrow", - "\nShoots a poisonous arrow.\nLets you control your \ - target.", - )), - _ => None, - }, + ToolKind::Bow(_) => Some(( + "Burst", + "\nLaunches a burst of arrows at the top \nof a running leap.", + )), + ToolKind::Debug(_) => Some(( + "Possessing Arrow", + "\nShoots a poisonous arrow.\nLets you control your target.", + )), _ => None, }, _ => None, @@ -621,10 +629,7 @@ impl<'a> Widget for Skillbar<'a> { ToolKind::Bow(_) => self.imgs.bow_m1, ToolKind::Sceptre(_) => self.imgs.heal_0, ToolKind::Staff(_) => self.imgs.fireball, - ToolKind::Debug(kind) => match kind.as_ref() { - "Boost" => self.imgs.flyingrod_m1, - _ => self.imgs.nothing, - }, + ToolKind::Debug(_) => self.imgs.flyingrod_m1, _ => self.imgs.nothing, }, _ => self.imgs.nothing, @@ -671,10 +676,7 @@ impl<'a> Widget for Skillbar<'a> { Some(ToolKind::Bow(_)) => self.imgs.bow_m2, Some(ToolKind::Sceptre(_)) => self.imgs.heal_bomb, Some(ToolKind::Staff(_)) => self.imgs.flamethrower, - Some(ToolKind::Debug(kind)) => match kind.as_ref() { - "Boost" => self.imgs.flyingrod_m2, - _ => self.imgs.nothing, - }, + Some(ToolKind::Debug(_)) => self.imgs.flyingrod_m2, _ => self.imgs.nothing, }) .w_h(36.0, 36.0) diff --git a/voxygen/src/hud/slots.rs b/voxygen/src/hud/slots.rs index ab31c28cda..56bd3dda56 100644 --- a/voxygen/src/hud/slots.rs +++ b/voxygen/src/hud/slots.rs @@ -116,10 +116,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot { ToolKind::Hammer(_) => Some(HotbarImage::HammerLeap), ToolKind::Axe(_) => Some(HotbarImage::AxeLeapSlash), ToolKind::Bow(_) => Some(HotbarImage::BowJumpBurst), - ToolKind::Debug(kind) => match kind.as_ref() { - "Boost" => Some(HotbarImage::SnakeArrow), - _ => None, - }, + ToolKind::Debug(_) => Some(HotbarImage::SnakeArrow), ToolKind::Sword(_) => Some(HotbarImage::SwordWhirlwind), _ => None, }, From 555bc559f5519a12d63c6ecd04e4a5096d924444 Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Sat, 17 Oct 2020 11:11:53 -0500 Subject: [PATCH 02/10] Axe no longer sets vertical velocity to 0, but instead preserves vertical velocity. --- common/src/states/spin_melee.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 8c97b99c0a..28c07f43fd 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -1,7 +1,10 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, - sys::character_behavior::{CharacterBehavior, JoinData}, + sys::{ + character_behavior::{CharacterBehavior, JoinData}, + phys::GRAVITY, + }, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -56,7 +59,8 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); if self.static_data.is_helicopter { - update.vel.0 = Vec3::new(data.inputs.move_dir.x, data.inputs.move_dir.y, 0.0) * 5.0; + update.vel.0 = Vec3::new(0.0, 0.0, update.vel.0.z + GRAVITY * data.dt.0) + + data.inputs.move_dir * 5.0; } // Allows for other states to interrupt this state From 15286a094a2cda17544f5a734d6218507b596b42 Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Sat, 17 Oct 2020 11:29:15 -0500 Subject: [PATCH 03/10] Leaps now require a non-negative vertical velocity to use. --- common/src/comp/ability.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 32659deaee..51844340a2 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -234,10 +234,13 @@ impl CharacterAbility { .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok(), - CharacterAbility::LeapMelee { energy_cost, .. } => update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok(), + CharacterAbility::LeapMelee { energy_cost, .. } => { + update.vel.0.z >= 0.0 + && update + .energy + .try_change_by(-(*energy_cost as i32), EnergySource::Ability) + .is_ok() + }, CharacterAbility::SpinMelee { energy_cost, .. } => update .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) @@ -250,10 +253,15 @@ impl CharacterAbility { .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok(), - CharacterAbility::RepeaterRanged { energy_cost, .. } => update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok(), + CharacterAbility::RepeaterRanged { + energy_cost, leap, .. + } => { + (leap.is_none() || update.vel.0.z >= 0.0) + && update + .energy + .try_change_by(-(*energy_cost as i32), EnergySource::Ability) + .is_ok() + }, CharacterAbility::Shockwave { energy_cost, .. } => update .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) From 844e6f2b60a8290429eacff78bacda4cc13213e8 Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Sat, 17 Oct 2020 11:39:25 -0500 Subject: [PATCH 04/10] Slightly nerfed sword dash. Reduced particle count on fire aoe by factor of 3. --- common/src/comp/inventory/item/tool.rs | 4 ++-- voxygen/src/scene/particle.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 4cc50a926d..85ed41c40e 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -173,8 +173,8 @@ impl Tool { }, DashMelee { energy_cost: 200, - base_damage: (120.0 * self.base_power()) as u32, - max_damage: (260.0 * self.base_power()) as u32, + base_damage: (100.0 * self.base_power()) as u32, + max_damage: (250.0 * self.base_power()) as u32, base_knockback: 10.0, max_knockback: 20.0, range: 5.0, diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index e9002961a5..e757a727f6 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -594,8 +594,8 @@ impl ParticleMgr { )); } } else { - for d in 0..10 * distance as i32 { - let arc_position = theta - radians / 2.0 + dtheta * d as f32 / 10.0; + for d in 0..3 * distance as i32 { + let arc_position = theta - radians / 2.0 + dtheta * d as f32 / 3.0; let position = pos.0 + distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0); From a7e3e55a1299e531e51ca7244e0465535648b795 Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Sat, 17 Oct 2020 17:42:43 -0500 Subject: [PATCH 05/10] Transitioned damage and healing from u32/i32s to enums. --- common/src/combat.rs | 161 +++++++++++++++++++++++ common/src/comp/beam.rs | 5 +- common/src/comp/character_state.rs | 4 +- common/src/comp/damage.rs | 79 ----------- common/src/comp/inventory/item/tool.rs | 12 +- common/src/comp/mod.rs | 2 - common/src/comp/projectile.rs | 4 +- common/src/comp/shockwave.rs | 4 +- common/src/lib.rs | 2 + common/src/metrics.rs | 2 +- common/src/states/basic_beam.rs | 14 +- common/src/states/basic_melee.rs | 7 +- common/src/states/charged_melee.rs | 10 +- common/src/states/charged_ranged.rs | 6 +- common/src/states/combo_melee.rs | 13 +- common/src/states/dash_melee.rs | 4 +- common/src/states/leap_melee.rs | 7 +- common/src/states/shockwave.rs | 6 +- common/src/states/spin_melee.rs | 7 +- common/src/sys/beam.rs | 57 +++----- common/src/sys/{combat.rs => melee.rs} | 52 +++----- common/src/sys/mod.rs | 6 +- common/src/sys/projectile.rs | 78 +++++------ common/src/sys/shockwave.rs | 39 ++---- server/src/events/entity_manipulation.rs | 63 +++------ server/src/lib.rs | 10 +- 26 files changed, 331 insertions(+), 323 deletions(-) create mode 100644 common/src/combat.rs delete mode 100644 common/src/comp/damage.rs rename common/src/sys/{combat.rs => melee.rs} (74%) diff --git a/common/src/combat.rs b/common/src/combat.rs new file mode 100644 index 0000000000..3702d2e978 --- /dev/null +++ b/common/src/combat.rs @@ -0,0 +1,161 @@ +use crate::{ + comp::{HealthChange, HealthSource, Loadout}, + sync::Uid, +}; +use serde::{Deserialize, Serialize}; + +pub const BLOCK_EFFICIENCY: f32 = 0.9; + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Damages { + pub enemy: Option<Damage>, + pub group: Option<Damage>, +} + +impl Damages { + pub fn new(enemy: Option<Damage>, group: Option<Damage>) -> Self { Damages { enemy, group } } +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Damage { + Melee(f32), + Healing(f32), + Projectile(f32), + Explosion(f32), + Falling(f32), + Shockwave(f32), + Energy(f32), +} + +impl Damage { + pub fn modify_damage( + self, + block: bool, + loadout: Option<&Loadout>, + uid: Option<Uid>, + ) -> HealthChange { + match self { + Damage::Melee(damage) => { + let mut damage = damage; + // Critical hit + let mut critdamage = 0.0; + if rand::random() { + critdamage = damage * 0.3; + } + // Block + if block { + damage *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + let damage_reduction = if let Some(loadout) = loadout { + loadout.get_damage_reduction() + } else { + 0.0 + }; + damage *= 1.0 - damage_reduction; + + // Critical damage applies after armor for melee + if (damage_reduction - 1.0).abs() > f32::EPSILON { + damage += critdamage; + } + + HealthChange { + amount: -damage as i32, + cause: HealthSource::Attack { by: uid.unwrap() }, + } + }, + Damage::Projectile(damage) => { + let mut damage = damage; + // Critical hit + if rand::random() { + damage *= 1.2; + } + // Block + if block { + damage *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + let damage_reduction = if let Some(loadout) = loadout { + loadout.get_damage_reduction() + } else { + 0.0 + }; + damage *= 1.0 - damage_reduction; + + HealthChange { + amount: -damage as i32, + cause: HealthSource::Projectile { owner: uid }, + } + }, + Damage::Explosion(damage) => { + let mut damage = damage; + // Block + if block { + damage *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + let damage_reduction = if let Some(loadout) = loadout { + loadout.get_damage_reduction() + } else { + 0.0 + }; + damage *= 1.0 - damage_reduction; + + HealthChange { + amount: -damage as i32, + cause: HealthSource::Explosion { owner: uid }, + } + }, + Damage::Shockwave(damage) => { + let mut damage = damage; + // Armor + let damage_reduction = if let Some(loadout) = loadout { + loadout.get_damage_reduction() + } else { + 0.0 + }; + damage *= 1.0 - damage_reduction; + + HealthChange { + amount: -damage as i32, + cause: HealthSource::Attack { by: uid.unwrap() }, + } + }, + Damage::Energy(damage) => { + let mut damage = damage; + // Armor + let damage_reduction = if let Some(loadout) = loadout { + loadout.get_damage_reduction() + } else { + 0.0 + }; + damage *= 1.0 - damage_reduction; + + HealthChange { + amount: -damage as i32, + cause: HealthSource::Energy { owner: uid }, + } + }, + Damage::Healing(heal) => HealthChange { + amount: heal as i32, + cause: HealthSource::Healing { by: uid }, + }, + Damage::Falling(damage) => { + let mut damage = damage; + // Armor + let damage_reduction = if let Some(loadout) = loadout { + loadout.get_damage_reduction() + } else { + 0.0 + }; + if (damage_reduction - 1.0).abs() < f32::EPSILON { + damage = 0.0; + } + HealthChange { + amount: -damage as i32, + cause: HealthSource::World, + } + }, + } + } +} diff --git a/common/src/comp/beam.rs b/common/src/comp/beam.rs index 1a0d311ea4..2c15b7fede 100644 --- a/common/src/comp/beam.rs +++ b/common/src/comp/beam.rs @@ -1,4 +1,4 @@ -use crate::sync::Uid; +use crate::{sync::Uid, Damages}; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; @@ -8,8 +8,7 @@ use std::time::Duration; pub struct Properties { pub angle: f32, pub speed: f32, - pub damage: u32, - pub heal: u32, + pub damages: Damages, pub lifesteal_eff: f32, pub energy_regen: u32, pub energy_cost: u32, diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 53ecc899c3..7b459b60ee 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -3,6 +3,7 @@ use crate::{ event::{LocalEvent, ServerEvent}, states::*, sys::character_behavior::JoinData, + Damages, }; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage, VecStorage}; @@ -152,8 +153,7 @@ impl Component for CharacterState { #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Attacking { - pub base_damage: u32, - pub base_heal: u32, + pub damages: Damages, pub range: f32, pub max_angle: f32, pub applied: bool, diff --git a/common/src/comp/damage.rs b/common/src/comp/damage.rs deleted file mode 100644 index 6fae9d3e71..0000000000 --- a/common/src/comp/damage.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::comp::Loadout; -use serde::{Deserialize, Serialize}; - -pub const BLOCK_EFFICIENCY: f32 = 0.9; - -pub struct Damage { - pub healthchange: f32, - pub source: DamageSource, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum DamageSource { - Melee, - Healing, - Projectile, - Explosion, - Falling, - Shockwave, - Energy, -} - -impl Damage { - pub fn modify_damage(&mut self, block: bool, loadout: &Loadout) { - match self.source { - DamageSource::Melee => { - // Critical hit - let mut critdamage = 0.0; - if rand::random() { - critdamage = self.healthchange * 0.3; - } - // Block - if block { - self.healthchange *= 1.0 - BLOCK_EFFICIENCY - } - // Armor - let damage_reduction = loadout.get_damage_reduction(); - self.healthchange *= 1.0 - damage_reduction; - - // Critical damage applies after armor for melee - if (damage_reduction - 1.0).abs() > f32::EPSILON { - self.healthchange += critdamage; - } - }, - DamageSource::Projectile => { - // Critical hit - if rand::random() { - self.healthchange *= 1.2; - } - // Block - if block { - self.healthchange *= 1.0 - BLOCK_EFFICIENCY - } - // Armor - let damage_reduction = loadout.get_damage_reduction(); - self.healthchange *= 1.0 - damage_reduction; - }, - DamageSource::Explosion => { - // Block - if block { - self.healthchange *= 1.0 - BLOCK_EFFICIENCY - } - // Armor - let damage_reduction = loadout.get_damage_reduction(); - self.healthchange *= 1.0 - damage_reduction; - }, - DamageSource::Shockwave => { - // Armor - let damage_reduction = loadout.get_damage_reduction(); - self.healthchange *= 1.0 - damage_reduction; - }, - DamageSource::Energy => { - // Armor - let damage_reduction = loadout.get_damage_reduction(); - self.healthchange *= 1.0 - damage_reduction; - }, - _ => {}, - } - } -} diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 85ed41c40e..f8e37ce635 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -4,7 +4,7 @@ use crate::{ comp::{body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile}, states::combo_melee, - Explosion, + Damage, Damages, Explosion, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -298,7 +298,10 @@ impl Tool { projectile: Projectile { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ - projectile::Effect::Damage((-40.0 * self.base_power()) as i32), + projectile::Effect::Damages(Damages::new( + Some(Damage::Projectile(40.0 * self.base_power())), + None, + )), projectile::Effect::Knockback(10.0), projectile::Effect::RewardEnergy(50), projectile::Effect::Vanish, @@ -338,7 +341,10 @@ impl Tool { projectile: Projectile { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ - projectile::Effect::Damage((-40.0 * self.base_power()) as i32), + projectile::Effect::Damages(Damages::new( + Some(Damage::Projectile(40.0 * self.base_power())), + None, + )), projectile::Effect::Knockback(10.0), projectile::Effect::RewardEnergy(50), projectile::Effect::Vanish, diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 329546a54e..3aa9556d99 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -7,7 +7,6 @@ pub mod buff; mod character_state; pub mod chat; mod controller; -mod damage; mod energy; pub mod group; mod inputs; @@ -44,7 +43,6 @@ pub use controller::{ Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, Input, InventoryManip, MountState, Mounting, }; -pub use damage::{Damage, DamageSource}; pub use energy::{Energy, EnergySource}; pub use group::Group; pub use inputs::CanBuild; diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 6241af9c01..121a6bdbb9 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -1,4 +1,4 @@ -use crate::{sync::Uid, Explosion}; +use crate::{sync::Uid, Damages, Explosion}; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; @@ -6,7 +6,7 @@ use std::time::Duration; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Effect { - Damage(i32), + Damages(Damages), Knockback(f32), RewardEnergy(u32), Explode(Explosion), diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs index 78e38e44bf..15161bc673 100644 --- a/common/src/comp/shockwave.rs +++ b/common/src/comp/shockwave.rs @@ -1,4 +1,4 @@ -use crate::sync::Uid; +use crate::{sync::Uid, Damages}; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; @@ -9,7 +9,7 @@ pub struct Properties { pub angle: f32, pub vertical_angle: f32, pub speed: f32, - pub damage: u32, + pub damages: Damages, pub knockback: f32, pub requires_ground: bool, pub duration: Duration, diff --git a/common/src/lib.rs b/common/src/lib.rs index 4ea0d8e30c..407328bd66 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -22,6 +22,7 @@ pub mod astar; pub mod character; pub mod clock; pub mod cmd; +pub mod combat; pub mod comp; pub mod effect; pub mod event; @@ -51,5 +52,6 @@ pub mod util; pub mod vol; pub mod volumes; +pub use combat::{Damage, Damages}; pub use explosion::Explosion; pub use loadout_builder::LoadoutBuilder; diff --git a/common/src/metrics.rs b/common/src/metrics.rs index 672f13d7c5..e2ec3ec27e 100644 --- a/common/src/metrics.rs +++ b/common/src/metrics.rs @@ -9,5 +9,5 @@ pub struct SysMetrics { pub stats_ns: AtomicI64, pub phys_ns: AtomicI64, pub projectile_ns: AtomicI64, - pub combat_ns: AtomicI64, + pub melee_ns: AtomicI64, } diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index e215fae41f..37c84d6264 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -4,6 +4,7 @@ use crate::{ states::utils::*, sync::Uid, sys::character_behavior::{CharacterBehavior, JoinData}, + Damage, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -108,10 +109,12 @@ 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 = - (self.static_data.base_dps as f32 / self.static_data.tick_rate) as u32; - let heal = - (self.static_data.base_hps as f32 / self.static_data.tick_rate) as u32; + let damage = Damage::Energy( + self.static_data.base_dps as f32 / self.static_data.tick_rate, + ); + let heal = Damage::Healing( + self.static_data.base_hps as f32 / self.static_data.tick_rate, + ); let energy_regen = (self.static_data.energy_regen as f32 / self.static_data.tick_rate) as u32; let energy_cost = @@ -121,8 +124,7 @@ impl CharacterBehavior for Data { let properties = beam::Properties { angle: self.static_data.max_angle.to_radians(), speed, - damage, - heal, + damages: Damages::new(Some(damage), Some(heal)), lifesteal_eff: self.static_data.lifesteal_eff, energy_regen, energy_cost, diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index dc3f6a4041..e99ec7a03d 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -2,6 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, + Damage, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -79,8 +80,10 @@ impl CharacterBehavior for Data { // Hit attempt data.updater.insert(data.entity, Attacking { - base_damage: self.static_data.base_damage, - base_heal: 0, + damages: Damages::new( + Some(Damage::Melee(self.static_data.base_damage as f32)), + None, + ), range: self.static_data.range, max_angle: 180_f32.to_radians(), applied: false, diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index e266fe99bf..62f5b3e3fe 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -2,6 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::{StageSection, *}, sys::character_behavior::*, + Damage, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -115,17 +116,16 @@ impl CharacterBehavior for Data { }, StageSection::Swing => { if !self.exhausted { - let damage = self.static_data.initial_damage - + ((self.static_data.max_damage - self.static_data.initial_damage) as f32 - * self.charge_amount) as u32; + let damage = self.static_data.initial_damage as f32 + + (self.static_data.max_damage - self.static_data.initial_damage) as f32 + * self.charge_amount; let knockback = self.static_data.initial_knockback + (self.static_data.max_knockback - self.static_data.initial_knockback) * self.charge_amount; // Hit attempt data.updater.insert(data.entity, Attacking { - base_damage: damage as u32, - base_heal: 0, + damages: Damages::new(Some(Damage::Melee(damage)), None), range: self.static_data.range, max_angle: self.static_data.max_angle.to_radians(), applied: false, diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 5413eaa386..5805338bc8 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -6,6 +6,7 @@ use crate::{ event::ServerEvent, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, + Damage, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -97,7 +98,10 @@ impl CharacterBehavior for Data { let mut projectile = Projectile { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ - projectile::Effect::Damage(-damage as i32), + projectile::Effect::Damages(Damages::new( + Some(Damage::Projectile(damage)), + None, + )), projectile::Effect::Knockback(knockback), projectile::Effect::Vanish, ], diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index fd59e00b3f..bf5610a5e4 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -2,6 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, + Damage, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -125,13 +126,13 @@ impl CharacterBehavior for Data { }); // Hit attempt + let damage = self.static_data.stage_data[stage_index].max_damage.min( + self.static_data.stage_data[stage_index].base_damage + + self.combo / self.static_data.num_stages + * self.static_data.stage_data[stage_index].damage_increase, + ); data.updater.insert(data.entity, Attacking { - base_damage: self.static_data.stage_data[stage_index].max_damage.min( - self.static_data.stage_data[stage_index].base_damage - + self.combo / self.static_data.num_stages - * self.static_data.stage_data[stage_index].damage_increase, - ), - base_heal: 0, + damages: Damages::new(Some(Damage::Melee(damage as f32)), None), range: self.static_data.stage_data[stage_index].range, max_angle: self.static_data.stage_data[stage_index].angle.to_radians(), applied: false, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 2b09a10203..278a2568e4 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -2,6 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, + Damage, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -121,8 +122,7 @@ impl CharacterBehavior for Data { * charge_frac + self.static_data.base_knockback; data.updater.insert(data.entity, Attacking { - base_damage: damage as u32, - base_heal: 0, + damages: Damages::new(Some(Damage::Melee(damage)), None), range: self.static_data.range, max_angle: self.static_data.angle.to_radians(), applied: false, diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 4f76f22daa..1df206992a 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -2,6 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, StateUpdate}, states::utils::{StageSection, *}, sys::character_behavior::{CharacterBehavior, JoinData}, + Damage, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -141,8 +142,10 @@ impl CharacterBehavior for Data { if !self.exhausted { // Hit attempt, when animation plays data.updater.insert(data.entity, Attacking { - base_damage: self.static_data.base_damage, - base_heal: 0, + damages: Damages::new( + Some(Damage::Melee(self.static_data.base_damage as f32)), + None, + ), range: self.static_data.range, max_angle: self.static_data.max_angle.to_radians(), applied: false, diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index fc2f72e10c..ab3c72e905 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -3,6 +3,7 @@ use crate::{ event::ServerEvent, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, + Damage, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -70,7 +71,10 @@ impl CharacterBehavior for Data { vertical_angle: self.static_data.shockwave_vertical_angle, speed: self.static_data.shockwave_speed, duration: self.static_data.shockwave_duration, - damage: self.static_data.damage, + damages: Damages::new( + Some(Damage::Shockwave(self.static_data.damage as f32)), + None, + ), knockback: self.static_data.knockback, requires_ground: self.static_data.requires_ground, owner: Some(*data.uid), diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 28c07f43fd..168b71cab7 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -5,6 +5,7 @@ use crate::{ character_behavior::{CharacterBehavior, JoinData}, phys::GRAVITY, }, + Damage, Damages, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -110,8 +111,10 @@ impl CharacterBehavior for Data { }); // Hit attempt data.updater.insert(data.entity, Attacking { - base_damage: self.static_data.base_damage, - base_heal: 0, + damages: Damages::new( + Some(Damage::Melee(self.static_data.base_damage as f32)), + None, + ), range: self.static_data.range, max_angle: 180_f32.to_radians(), applied: false, diff --git a/common/src/sys/beam.rs b/common/src/sys/beam.rs index d39a8e299f..e6abc2ecf0 100644 --- a/common/src/sys/beam.rs +++ b/common/src/sys/beam.rs @@ -1,11 +1,12 @@ use crate::{ comp::{ - group, Beam, BeamSegment, Body, CharacterState, Damage, DamageSource, Energy, EnergySource, - HealthChange, HealthSource, Last, Loadout, Ori, Pos, Scale, Stats, + group, Beam, BeamSegment, Body, CharacterState, Energy, EnergySource, HealthChange, + HealthSource, Last, Loadout, Ori, Pos, Scale, Stats, }, event::{EventBus, ServerEvent}, state::{DeltaTime, Time}, sync::{Uid, UidAllocator}, + Damage, }; use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage}; use std::time::Duration; @@ -166,54 +167,31 @@ impl<'a> System<'a> for Sys { if Some(*uid_b) == beam_segment.owner { continue; } - // Don't heal if outside group - // Don't damage in the same group - let is_damage = !same_group && (beam_segment.damage > 0); - let is_heal = same_group && (beam_segment.heal > 0); - if !is_heal && !is_damage { + + let damage = if !same_group && beam_segment.damages.enemy.is_some() { + beam_segment.damages.enemy.unwrap() + } else if same_group && beam_segment.damages.group.is_some() { + beam_segment.damages.group.unwrap() + } else { continue; - } - - // Weapon gives base damage - let source = if is_heal { - DamageSource::Healing - } else { - DamageSource::Energy - }; - let healthchange = if is_heal { - beam_segment.heal as f32 - } else { - -(beam_segment.damage as f32) - }; - - let mut damage = Damage { - healthchange, - source, }; let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) // TODO: investigate whether this calculation is proper for beams && ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; - if let Some(loadout) = loadouts.get(b) { - damage.modify_damage(block, loadout); - } + let change = damage.modify_damage(block, loadouts.get(b), beam_segment.owner); - if is_damage { + if let Damage::Energy(_) = damage { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, - change: HealthChange { - amount: damage.healthchange as i32, - cause: HealthSource::Energy { - owner: beam_segment.owner, - }, - }, + change, }); if beam_segment.lifesteal_eff > 0.0 { server_emitter.emit(ServerEvent::Damage { uid: beam_segment.owner.unwrap_or(*uid), change: HealthChange { - amount: (-damage.healthchange * beam_segment.lifesteal_eff) + amount: (-change.amount as f32 * beam_segment.lifesteal_eff) as i32, cause: HealthSource::Healing { by: beam_segment.owner, @@ -228,7 +206,7 @@ impl<'a> System<'a> for Sys { ); } } - if is_heal { + if let Damage::Healing(_) = damage { if let Some(energy_mut) = beam_owner.and_then(|o| energies.get_mut(o)) { if energy_mut .try_change_by( @@ -239,12 +217,7 @@ impl<'a> System<'a> for Sys { { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, - change: HealthChange { - amount: damage.healthchange as i32, - cause: HealthSource::Healing { - by: beam_segment.owner, - }, - }, + change, }); } } diff --git a/common/src/sys/combat.rs b/common/src/sys/melee.rs similarity index 74% rename from common/src/sys/combat.rs rename to common/src/sys/melee.rs index cfa469a910..66d2c20b32 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/melee.rs @@ -1,8 +1,5 @@ use crate::{ - comp::{ - buff, group, Attacking, Body, CharacterState, Damage, DamageSource, HealthChange, - HealthSource, Loadout, Ori, Pos, Scale, Stats, - }, + comp::{buff, group, Attacking, Body, CharacterState, Loadout, Ori, Pos, Scale, Stats}, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, span, @@ -59,7 +56,7 @@ impl<'a> System<'a> for Sys { ): Self::SystemData, ) { let start_time = std::time::Instant::now(); - span!(_guard, "run", "combat::Sys::run"); + span!(_guard, "run", "melee::Sys::run"); let mut server_emitter = server_bus.emitter(); let mut _local_emitter = local_bus.emitter(); // Attacks @@ -113,56 +110,36 @@ impl<'a> System<'a> for Sys { .get(entity) .map(|group_a| Some(group_a) == groups.get(b)) .unwrap_or(false); - // Don't heal if outside group - // Don't damage in the same group - let is_damage = !same_group && (attack.base_damage > 0); - let is_heal = same_group && (attack.base_heal > 0); - if !is_heal && !is_damage { - continue; - } - // Weapon gives base damage - let (source, healthchange) = if is_heal { - (DamageSource::Healing, attack.base_heal as f32) + let damage = if !same_group && attack.damages.enemy.is_some() { + attack.damages.enemy.unwrap() + } else if same_group && attack.damages.group.is_some() { + attack.damages.group.unwrap() } else { - (DamageSource::Melee, -(attack.base_damage as f32)) - }; - let mut damage = Damage { - healthchange, - source, + continue; }; let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) && ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; - if let Some(loadout) = loadouts.get(b) { - damage.modify_damage(block, loadout); - } + let change = damage.modify_damage(block, loadouts.get(b), Some(*uid)); - if damage.healthchange != 0.0 { - let cause = if is_heal { - HealthSource::Healing { by: Some(*uid) } - } else { - HealthSource::Attack { by: *uid } - }; + if change.amount != 0 { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, - change: HealthChange { - amount: damage.healthchange as i32, - cause, - }, + change, }); // Apply bleeding buff on melee hits with 10% chance // TODO: Don't have buff uniformly applied on all melee attacks - if thread_rng().gen::<f32>() < 0.1 { + if change.amount < 0 && thread_rng().gen::<f32>() < 0.1 { use buff::*; server_emitter.emit(ServerEvent::Buff { entity: b, buff_change: BuffChange::Add(Buff::new( BuffKind::Bleeding, BuffData { - strength: attack.base_damage as f32 / 10.0, + strength: -change.amount as f32 / 10.0, duration: Some(Duration::from_secs(10)), }, vec![BuffCategory::Physical], @@ -172,7 +149,8 @@ impl<'a> System<'a> for Sys { } attack.hit_count += 1; } - if attack.knockback != 0.0 && damage.healthchange != 0.0 { + + if attack.knockback != 0.0 && change.amount != 0 { let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); server_emitter.emit(ServerEvent::Knockback { entity: b, @@ -183,7 +161,7 @@ impl<'a> System<'a> for Sys { } } } - sys_metrics.combat_ns.store( + sys_metrics.melee_ns.store( start_time.elapsed().as_nanos() as i64, std::sync::atomic::Ordering::Relaxed, ); diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs index 98c70fd145..50f222744d 100644 --- a/common/src/sys/mod.rs +++ b/common/src/sys/mod.rs @@ -2,8 +2,8 @@ pub mod agent; mod beam; mod buff; pub mod character_behavior; -pub mod combat; pub mod controller; +pub mod melee; mod mount; pub mod phys; mod projectile; @@ -15,7 +15,7 @@ use specs::DispatcherBuilder; // System names pub const CHARACTER_BEHAVIOR_SYS: &str = "character_behavior_sys"; -pub const COMBAT_SYS: &str = "combat_sys"; +pub const MELEE_SYS: &str = "melee_sys"; pub const AGENT_SYS: &str = "agent_sys"; pub const BEAM_SYS: &str = "beam_sys"; pub const CONTROLLER_SYS: &str = "controller_sys"; @@ -39,5 +39,5 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(projectile::Sys, PROJECTILE_SYS, &[PHYS_SYS]); dispatch_builder.add(shockwave::Sys, SHOCKWAVE_SYS, &[PHYS_SYS]); dispatch_builder.add(beam::Sys, BEAM_SYS, &[PHYS_SYS]); - dispatch_builder.add(combat::Sys, COMBAT_SYS, &[PROJECTILE_SYS]); + dispatch_builder.add(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]); } diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index 5936575c33..59d8e8cbe2 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - projectile, Damage, DamageSource, Energy, EnergySource, Group, HealthChange, HealthSource, - Loadout, Ori, PhysicsState, Pos, Projectile, Vel, + projectile, Energy, EnergySource, Group, HealthSource, Loadout, Ori, PhysicsState, Pos, + Projectile, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, @@ -73,20 +73,21 @@ impl<'a> System<'a> for Sys { { // Hit entity for other in physics.touch_entities.iter().copied() { + let same_group = projectile + .owner + // Note: somewhat inefficient since we do the lookup for every touching + // entity, but if we pull this out of the loop we would want to do it only + // if there is at least one touching entity + .and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into())) + .and_then(|e| groups.get(e)) + .map_or(false, |owner_group| + Some(owner_group) == uid_allocator + .retrieve_entity_internal(other.into()) + .and_then(|e| groups.get(e)) + ); if projectile.ignore_group // Skip if in the same group - && projectile - .owner - // Note: somewhat inefficient since we do the lookup for every touching - // entity, but if we pull this out of the loop we would want to do it only - // if there is at least one touching entity - .and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into())) - .and_then(|e| groups.get(e)) - .map_or(false, |owner_group| - Some(owner_group) == uid_allocator - .retrieve_entity_internal(other.into()) - .and_then(|e| groups.get(e)) - ) + && same_group { continue; } @@ -97,40 +98,25 @@ impl<'a> System<'a> for Sys { for effect in projectile.hit_entity.drain(..) { match effect { - projectile::Effect::Damage(healthchange) => { - let owner_uid = projectile.owner.unwrap(); - let mut damage = Damage { - healthchange: healthchange as f32, - source: DamageSource::Projectile, - }; - - let other_entity = uid_allocator.retrieve_entity_internal(other.into()); - if let Some(loadout) = other_entity.and_then(|e| loadouts.get(e)) { - damage.modify_damage(false, loadout); + projectile::Effect::Damages(damages) => { + if Some(other) == projectile.owner { + continue; } + let damage = if !same_group && damages.enemy.is_some() { + damages.enemy.unwrap() + } else if same_group && damages.group.is_some() { + damages.group.unwrap() + } else { + continue; + }; + let other_entity_loadout = uid_allocator + .retrieve_entity_internal(other.into()) + .and_then(|e| loadouts.get(e)); + let change = + damage.modify_damage(false, other_entity_loadout, projectile.owner); - if other != owner_uid { - if damage.healthchange < 0.0 { - server_emitter.emit(ServerEvent::Damage { - uid: other, - change: HealthChange { - amount: damage.healthchange as i32, - cause: HealthSource::Projectile { - owner: Some(owner_uid), - }, - }, - }); - } else if damage.healthchange > 0.0 { - server_emitter.emit(ServerEvent::Damage { - uid: other, - change: HealthChange { - amount: damage.healthchange as i32, - cause: HealthSource::Healing { - by: Some(owner_uid), - }, - }, - }); - } + if change.amount != 0 { + server_emitter.emit(ServerEvent::Damage { uid: other, change }); } }, projectile::Effect::Knockback(knockback) => { diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index 1dbf0530cb..dbdea44459 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - group, Body, CharacterState, Damage, DamageSource, HealthChange, HealthSource, Last, - Loadout, Ori, PhysicsState, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats, + group, Body, CharacterState, HealthSource, Last, Loadout, Ori, PhysicsState, Pos, Scale, + Shockwave, ShockwaveHitEntities, Stats, }, event::{EventBus, LocalEvent, ServerEvent}, state::{DeltaTime, Time}, @@ -189,42 +189,31 @@ impl<'a> System<'a> for Sys { }) } && (pos_b_ground - pos.0).angle_between(pos_b.0 - pos.0) < max_angle - && (!shockwave.requires_ground || physics_state_b.on_ground) - && !same_group; + && (!shockwave.requires_ground || physics_state_b.on_ground); if hit { - let mut damage = Damage { - healthchange: -(shockwave.damage as f32), - source: DamageSource::Shockwave, + let damage = if !same_group && shockwave.damages.enemy.is_some() { + shockwave.damages.enemy.unwrap() + } else if same_group && shockwave.damages.group.is_some() { + shockwave.damages.group.unwrap() + } else { + continue; }; let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) && ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; - if let Some(loadout) = loadouts.get(b) { - damage.modify_damage(block, loadout); - } + let owner_uid = shockwave.owner.unwrap_or(*uid); + let change = damage.modify_damage(block, loadouts.get(b), Some(owner_uid)); - if damage.healthchange != 0.0 { - let cause = if damage.healthchange < 0.0 { - HealthSource::Attack { - by: shockwave.owner.unwrap_or(*uid), - } - } else { - HealthSource::Healing { - by: Some(shockwave.owner.unwrap_or(*uid)), - } - }; + if change.amount != 0 { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, - change: HealthChange { - amount: damage.healthchange as i32, - cause, - }, + change, }); shockwave_hit_list.hit_entities.push(*uid_b); } - if shockwave.knockback != 0.0 && damage.healthchange != 0.0 { + if shockwave.knockback != 0.0 { let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); let impulse = if shockwave.knockback < 0.0 { shockwave.knockback diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index e32e7318b7..cb175c4c20 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -8,18 +8,17 @@ use common::{ comp::{ self, buff, chat::{KillSource, KillType}, - object, Alignment, Body, Damage, DamageSource, Group, HealthChange, HealthSource, Item, - Player, Pos, Stats, + object, Alignment, Body, Group, HealthChange, HealthSource, Item, Player, Pos, Stats, }, lottery::Lottery, msg::{PlayerListUpdate, ServerGeneral}, outcome::Outcome, state::BlockChange, sync::{Uid, UidAllocator, WorldSyncExt}, - sys::combat::BLOCK_ANGLE, + sys::melee::BLOCK_ANGLE, terrain::{Block, TerrainGrid}, vol::ReadVol, - Explosion, + Damage, Explosion, }; use comp::item::Reagent; use rand::prelude::*; @@ -456,17 +455,10 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>) if vel.z <= -30.0 { if let Some(stats) = state.ecs().write_storage::<comp::Stats>().get_mut(entity) { let falldmg = (vel.z.powi(2) / 20.0 - 40.0) * 10.0; - let mut damage = Damage { - healthchange: -falldmg, - source: DamageSource::Falling, - }; - if let Some(loadout) = state.ecs().read_storage::<comp::Loadout>().get(entity) { - damage.modify_damage(false, loadout); - } - stats.health.change_by(comp::HealthChange { - amount: damage.healthchange as i32, - cause: comp::HealthSource::World, - }); + let damage = Damage::Falling(falldmg); + let loadouts = state.ecs().read_storage::<comp::Loadout>(); + let change = damage.modify_damage(false, loadouts.get(entity), None); + stats.health.change_by(change); } } } @@ -576,43 +568,26 @@ pub fn handle_explosion( continue; } - // Weapon gives base damage - let source = if is_heal { - DamageSource::Healing - } else { - DamageSource::Explosion - }; let strength = 1.0 - distance_squared / explosion.radius.powi(2); - let healthchange = if is_heal { - explosion.min_heal as f32 - + (explosion.max_heal - explosion.min_heal) as f32 * strength + let damage = if is_heal { + Damage::Healing( + explosion.min_heal as f32 + + (explosion.max_heal - explosion.min_heal) as f32 * strength, + ) } else { - -(explosion.min_damage as f32 - + (explosion.max_damage - explosion.min_damage) as f32 * strength) - }; - - let mut damage = Damage { - healthchange, - source, + Damage::Explosion( + explosion.min_damage as f32 + + (explosion.max_damage - explosion.min_damage) as f32 * strength, + ) }; let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) && ori_b.0.angle_between(pos - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; - if let Some(loadout) = loadout_b { - damage.modify_damage(block, loadout); - } + let change = damage.modify_damage(block, loadout_b, owner); - if damage.healthchange != 0.0 { - let cause = if is_heal { - HealthSource::Healing { by: owner } - } else { - HealthSource::Explosion { owner } - }; - stats_b.health.change_by(HealthChange { - amount: damage.healthchange as i32, - cause, - }); + if change.amount != 0 { + stats_b.health.change_by(change); if let Some(owner) = owner_entity { if let Some(energy) = ecs.write_storage::<comp::Energy>().get_mut(owner) { energy diff --git a/server/src/lib.rs b/server/src/lib.rs index d2a6111f05..e3acfa2f02 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -717,7 +717,7 @@ impl Server { let stats_ns = res.stats_ns.load(Ordering::Relaxed); let phys_ns = res.phys_ns.load(Ordering::Relaxed); let projectile_ns = res.projectile_ns.load(Ordering::Relaxed); - let combat_ns = res.combat_ns.load(Ordering::Relaxed); + let melee_ns = res.melee_ns.load(Ordering::Relaxed); c.with_label_values(&[common::sys::AGENT_SYS]) .inc_by(agent_ns); @@ -733,8 +733,8 @@ impl Server { .inc_by(phys_ns); c.with_label_values(&[common::sys::PROJECTILE_SYS]) .inc_by(projectile_ns); - c.with_label_values(&[common::sys::COMBAT_SYS]) - .inc_by(combat_ns); + c.with_label_values(&[common::sys::MELEE_SYS]) + .inc_by(melee_ns); const NANOSEC_PER_SEC: f64 = Duration::from_secs(1).as_nanos() as f64; let h = &self.state_tick_metrics.state_tick_time_hist; @@ -752,8 +752,8 @@ impl Server { .observe(phys_ns as f64 / NANOSEC_PER_SEC); h.with_label_values(&[common::sys::PROJECTILE_SYS]) .observe(projectile_ns as f64 / NANOSEC_PER_SEC); - h.with_label_values(&[common::sys::COMBAT_SYS]) - .observe(combat_ns as f64 / NANOSEC_PER_SEC); + h.with_label_values(&[common::sys::MELEE_SYS]) + .observe(melee_ns as f64 / NANOSEC_PER_SEC); } // Report other info From 1ccbdec35cd1ec8dbeb919485d81f3e282df300d Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Sun, 18 Oct 2020 11:46:28 -0500 Subject: [PATCH 06/10] Tweaked dash melee some more. --- common/src/comp/ability.rs | 3 +- common/src/comp/inventory/item/tool.rs | 10 +- common/src/states/dash_melee.rs | 136 ++++++++++++------------- 3 files changed, 70 insertions(+), 79 deletions(-) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 51844340a2..453e905c05 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -452,8 +452,9 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { recover_duration: *recover_duration, is_interruptible: *is_interruptible, }, - end_charge: false, + auto_charge: false, timer: Duration::default(), + refresh_timer: Duration::default(), stage_section: StageSection::Buildup, exhausted: false, }), diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index f8e37ce635..c1b7833d55 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -173,16 +173,16 @@ impl Tool { }, DashMelee { energy_cost: 200, - base_damage: (100.0 * self.base_power()) as u32, - max_damage: (250.0 * self.base_power()) as u32, - base_knockback: 10.0, - max_knockback: 20.0, + base_damage: (120.0 * self.base_power()) as u32, + max_damage: (240.0 * self.base_power()) as u32, + base_knockback: 8.0, + max_knockback: 15.0, range: 5.0, angle: 45.0, energy_drain: 500, forward_speed: 4.0, buildup_duration: Duration::from_millis(250), - charge_duration: Duration::from_millis(400), + charge_duration: Duration::from_millis(600), swing_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(500), infinite_charge: true, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 278a2568e4..f117dc19b4 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -47,9 +47,11 @@ pub struct Data { /// character state pub static_data: StaticData, /// Whether the charge should end - pub end_charge: bool, + pub auto_charge: bool, /// Timer for each stage pub timer: Duration, + /// Timer used to limit how often another attack will be applied + pub refresh_timer: Duration, /// What section the character stage is in pub stage_section: StageSection, /// Whether the state should attempt attacking again @@ -80,11 +82,12 @@ impl CharacterBehavior for Data { // Build up update.character = CharacterState::DashMelee(Data { static_data: self.static_data, - end_charge: self.end_charge, + auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), + refresh_timer: self.refresh_timer, stage_section: self.stage_section, exhausted: self.exhausted, }); @@ -92,103 +95,86 @@ impl CharacterBehavior for Data { // Transitions to charge section of stage update.character = CharacterState::DashMelee(Data { static_data: self.static_data, - end_charge: self.end_charge, + auto_charge: !data.inputs.secondary.is_pressed(), timer: Duration::default(), + refresh_timer: self.refresh_timer, stage_section: StageSection::Charge, exhausted: self.exhausted, }); } }, StageSection::Charge => { - if (self.timer < self.static_data.charge_duration - || (self.static_data.infinite_charge && data.inputs.secondary.is_pressed())) + if (self.static_data.infinite_charge + || self.timer < self.static_data.charge_duration) + && (data.inputs.secondary.is_pressed() + || (self.auto_charge && self.timer < self.static_data.charge_duration)) && update.energy.current() > 0 - && !self.end_charge { // Forward movement forward_move(data, &mut update, 0.1, self.static_data.forward_speed); - // Hit attempt (also checks if player is moving) - if !self.exhausted && update.vel.0.distance_squared(Vec3::zero()) > 1.0 { - let charge_frac = (self.timer.as_secs_f32() - / self.static_data.charge_duration.as_secs_f32()) - .min(1.0); - let damage = (self.static_data.max_damage as f32 - - self.static_data.base_damage as f32) - * charge_frac - + self.static_data.base_damage as f32; - let knockback = (self.static_data.max_knockback - - self.static_data.base_knockback) - * charge_frac - + self.static_data.base_knockback; - data.updater.insert(data.entity, Attacking { - damages: Damages::new(Some(Damage::Melee(damage)), None), - range: self.static_data.range, - max_angle: self.static_data.angle.to_radians(), - applied: false, - hit_count: 0, - knockback, - }); - } - // This logic basically just decides if a charge should end, and prevents the // character state spamming attacks while checking if it has hit something if !self.exhausted { + // Hit attempt (also checks if player is moving) + if update.vel.0.distance_squared(Vec3::zero()) > 1.0 { + let charge_frac = (self.timer.as_secs_f32() + / self.static_data.charge_duration.as_secs_f32()) + .min(1.0); + let damage = (self.static_data.max_damage as f32 + - self.static_data.base_damage as f32) + * charge_frac + + self.static_data.base_damage as f32; + let knockback = (self.static_data.max_knockback + - self.static_data.base_knockback) + * charge_frac + + self.static_data.base_knockback; + data.updater.insert(data.entity, Attacking { + damages: Damages::new(Some(Damage::Melee(damage)), None), + range: self.static_data.range, + max_angle: self.static_data.angle.to_radians(), + applied: false, + hit_count: 0, + knockback, + }); + } update.character = CharacterState::DashMelee(Data { static_data: self.static_data, - end_charge: self.end_charge, + auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: StageSection::Charge, + refresh_timer: self.refresh_timer, + stage_section: self.stage_section, exhausted: true, }) - } else if let Some(attack) = data.attacking { - if attack.applied && attack.hit_count > 0 { - update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - end_charge: !self.static_data.infinite_charge, - timer: self - .timer - .checked_add(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - stage_section: StageSection::Charge, - exhausted: false, - }) - } else if attack.applied { - update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - end_charge: self.end_charge, - timer: self - .timer - .checked_add(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - stage_section: StageSection::Charge, - exhausted: false, - }) - } else { - update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - end_charge: !self.static_data.infinite_charge, - timer: self - .timer - .checked_add(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - stage_section: StageSection::Charge, - exhausted: self.exhausted, - }) - } + } else if self.refresh_timer < Duration::from_millis(50) { + update.character = CharacterState::DashMelee(Data { + static_data: self.static_data, + auto_charge: self.auto_charge, + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + refresh_timer: self + .refresh_timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + stage_section: self.stage_section, + exhausted: self.exhausted, + }) } else { update.character = CharacterState::DashMelee(Data { static_data: self.static_data, - end_charge: !self.static_data.infinite_charge, + auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: StageSection::Charge, - exhausted: self.exhausted, + refresh_timer: Duration::default(), + stage_section: self.stage_section, + exhausted: false, }) } @@ -201,8 +187,9 @@ impl CharacterBehavior for Data { // Transitions to swing section of stage update.character = CharacterState::DashMelee(Data { static_data: self.static_data, - end_charge: self.end_charge, + auto_charge: self.auto_charge, timer: Duration::default(), + refresh_timer: self.refresh_timer, stage_section: StageSection::Swing, exhausted: self.exhausted, }); @@ -213,11 +200,12 @@ impl CharacterBehavior for Data { // Swings update.character = CharacterState::DashMelee(Data { static_data: self.static_data, - end_charge: self.end_charge, + auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), + refresh_timer: self.refresh_timer, stage_section: self.stage_section, exhausted: self.exhausted, }); @@ -225,8 +213,9 @@ impl CharacterBehavior for Data { // Transitions to recover section of stage update.character = CharacterState::DashMelee(Data { static_data: self.static_data, - end_charge: self.end_charge, + auto_charge: self.auto_charge, timer: Duration::default(), + refresh_timer: self.refresh_timer, stage_section: StageSection::Recover, exhausted: self.exhausted, }); @@ -237,11 +226,12 @@ impl CharacterBehavior for Data { // Recover update.character = CharacterState::DashMelee(Data { static_data: self.static_data, - end_charge: self.end_charge, + auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), + refresh_timer: self.refresh_timer, stage_section: self.stage_section, exhausted: self.exhausted, }); From 981eee59369db089950bdf64182e2f0da70f50d6 Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Sun, 18 Oct 2020 13:21:58 -0500 Subject: [PATCH 07/10] Transitioned knockback to an enum. --- common/src/combat.rs | 25 +++++++++++++++++++++++++ common/src/comp/ability.rs | 3 ++- common/src/comp/character_state.rs | 4 ++-- common/src/comp/inventory/item/tool.rs | 11 +++++------ common/src/comp/projectile.rs | 4 ++-- common/src/comp/shockwave.rs | 4 ++-- common/src/lib.rs | 2 +- common/src/states/basic_melee.rs | 4 ++-- common/src/states/charged_melee.rs | 4 ++-- common/src/states/charged_ranged.rs | 4 ++-- common/src/states/combo_melee.rs | 6 ++++-- common/src/states/dash_melee.rs | 4 ++-- common/src/states/leap_melee.rs | 4 ++-- common/src/states/shockwave.rs | 4 ++-- common/src/states/spin_melee.rs | 4 ++-- common/src/sys/melee.rs | 5 ++--- common/src/sys/projectile.rs | 5 +---- common/src/sys/shockwave.rs | 14 ++++---------- common/src/util/dir.rs | 7 +++++++ 19 files changed, 71 insertions(+), 47 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 3702d2e978..e80de3abe6 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -1,8 +1,10 @@ use crate::{ comp::{HealthChange, HealthSource, Loadout}, sync::Uid, + util::Dir, }; use serde::{Deserialize, Serialize}; +use vek::*; pub const BLOCK_EFFICIENCY: f32 = 0.9; @@ -159,3 +161,26 @@ impl Damage { } } } + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Knockback { + Away(f32), + Towards(f32), + Up(f32), + TowardsUp(f32), +} + +impl Knockback { + pub fn get_knockback(self, dir: Dir) -> Vec3<f32> { + 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) + }, + Knockback::Up(strength) => strength * Vec3::unit_z(), + Knockback::TowardsUp(strength) => { + strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.85) + }, + } + } +} diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 453e905c05..ab0f92cd5d 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -8,6 +8,7 @@ use crate::{ *, }, sys::character_behavior::JoinData, + Knockback, }; use arraygen::Arraygen; use serde::{Deserialize, Serialize}; @@ -184,7 +185,7 @@ pub enum CharacterAbility { swing_duration: Duration, recover_duration: Duration, damage: u32, - knockback: f32, + knockback: Knockback, shockwave_angle: f32, shockwave_vertical_angle: f32, shockwave_speed: f32, diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 7b459b60ee..f439fbcfdf 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -3,7 +3,7 @@ use crate::{ event::{LocalEvent, ServerEvent}, states::*, sys::character_behavior::JoinData, - Damages, + Damages, Knockback, }; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage, VecStorage}; @@ -158,7 +158,7 @@ pub struct Attacking { pub max_angle: f32, pub applied: bool, pub hit_count: u32, - pub knockback: f32, + pub knockback: Knockback, } impl Component for Attacking { diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index c1b7833d55..78d0d40e96 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -4,7 +4,7 @@ use crate::{ comp::{body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile}, states::combo_melee, - Damage, Damages, Explosion, + Damage, Damages, Explosion, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -302,7 +302,7 @@ impl Tool { Some(Damage::Projectile(40.0 * self.base_power())), None, )), - projectile::Effect::Knockback(10.0), + projectile::Effect::Knockback(Knockback::Away(10.0)), projectile::Effect::RewardEnergy(50), projectile::Effect::Vanish, ], @@ -345,8 +345,7 @@ impl Tool { Some(Damage::Projectile(40.0 * self.base_power())), None, )), - projectile::Effect::Knockback(10.0), - projectile::Effect::RewardEnergy(50), + projectile::Effect::Knockback(Knockback::Away(10.0)), projectile::Effect::Vanish, ], time_left: Duration::from_secs(15), @@ -489,7 +488,7 @@ impl Tool { swing_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(300), damage: (200.0 * self.base_power()) as u32, - knockback: 25.0, + knockback: Knockback::Away(25.0), shockwave_angle: 360.0, shockwave_vertical_angle: 90.0, shockwave_speed: 20.0, @@ -530,7 +529,7 @@ impl Tool { swing_duration: Duration::from_millis(200), recover_duration: Duration::from_millis(800), damage: 500, - knockback: -40.0, + knockback: Knockback::TowardsUp(40.0), shockwave_angle: 90.0, shockwave_vertical_angle: 15.0, shockwave_speed: 20.0, diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 121a6bdbb9..3b5888a456 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -1,4 +1,4 @@ -use crate::{sync::Uid, Damages, Explosion}; +use crate::{sync::Uid, Damages, Explosion, Knockback}; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; @@ -7,7 +7,7 @@ use std::time::Duration; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Effect { Damages(Damages), - Knockback(f32), + Knockback(Knockback), RewardEnergy(u32), Explode(Explosion), Vanish, diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs index 15161bc673..2d5de87726 100644 --- a/common/src/comp/shockwave.rs +++ b/common/src/comp/shockwave.rs @@ -1,4 +1,4 @@ -use crate::{sync::Uid, Damages}; +use crate::{sync::Uid, Damages, Knockback}; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; @@ -10,7 +10,7 @@ pub struct Properties { pub vertical_angle: f32, pub speed: f32, pub damages: Damages, - pub knockback: f32, + pub knockback: Knockback, pub requires_ground: bool, pub duration: Duration, pub owner: Option<Uid>, diff --git a/common/src/lib.rs b/common/src/lib.rs index 407328bd66..01b986408a 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -52,6 +52,6 @@ pub mod util; pub mod vol; pub mod volumes; -pub use combat::{Damage, Damages}; +pub use combat::{Damage, Damages, Knockback}; pub use explosion::Explosion; pub use loadout_builder::LoadoutBuilder; diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index e99ec7a03d..744c30c3b8 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, + Damage, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -88,7 +88,7 @@ impl CharacterBehavior for Data { max_angle: 180_f32.to_radians(), applied: false, hit_count: 0, - knockback: self.static_data.knockback, + knockback: Knockback::Away(self.static_data.knockback), }); } 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 62f5b3e3fe..0da5ada515 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::{StageSection, *}, sys::character_behavior::*, - Damage, Damages, + Damage, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -130,7 +130,7 @@ impl CharacterBehavior for Data { max_angle: self.static_data.max_angle.to_radians(), applied: false, hit_count: 0, - knockback, + knockback: Knockback::Away(knockback), }); // Starts swinging diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 5805338bc8..3b19fa8268 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -6,7 +6,7 @@ use crate::{ event::ServerEvent, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, + Damage, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -102,7 +102,7 @@ impl CharacterBehavior for Data { Some(Damage::Projectile(damage)), None, )), - projectile::Effect::Knockback(knockback), + projectile::Effect::Knockback(Knockback::Away(knockback)), projectile::Effect::Vanish, ], time_left: Duration::from_secs(15), diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index bf5610a5e4..4ae0d24efd 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, + Damage, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -137,7 +137,9 @@ impl CharacterBehavior for Data { max_angle: self.static_data.stage_data[stage_index].angle.to_radians(), applied: false, hit_count: 0, - knockback: self.static_data.stage_data[stage_index].knockback, + knockback: Knockback::Away( + self.static_data.stage_data[stage_index].knockback, + ), }); } }, diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index f117dc19b4..fbd83a2652 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, + Damage, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -135,7 +135,7 @@ impl CharacterBehavior for Data { max_angle: self.static_data.angle.to_radians(), applied: false, hit_count: 0, - knockback, + knockback: Knockback::Away(knockback), }); } update.character = CharacterState::DashMelee(Data { diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 1df206992a..d6b6f297af 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -2,7 +2,7 @@ use crate::{ comp::{Attacking, CharacterState, StateUpdate}, states::utils::{StageSection, *}, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, + Damage, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -150,7 +150,7 @@ impl CharacterBehavior for Data { max_angle: self.static_data.max_angle.to_radians(), applied: false, hit_count: 0, - knockback: self.static_data.knockback, + knockback: Knockback::Away(self.static_data.knockback), }); update.character = CharacterState::LeapMelee(Data { diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index ab3c72e905..a9bb69cd07 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -3,7 +3,7 @@ use crate::{ event::ServerEvent, states::utils::*, sys::character_behavior::{CharacterBehavior, JoinData}, - Damage, Damages, + Damage, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -20,7 +20,7 @@ pub struct StaticData { /// Base damage pub damage: u32, /// Knockback - pub knockback: f32, + pub knockback: Knockback, /// Angle of the shockwave pub shockwave_angle: f32, /// Vertical angle of the shockwave diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 168b71cab7..73765d1607 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -5,7 +5,7 @@ use crate::{ character_behavior::{CharacterBehavior, JoinData}, phys::GRAVITY, }, - Damage, Damages, + Damage, Damages, Knockback, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -119,7 +119,7 @@ impl CharacterBehavior for Data { max_angle: 180_f32.to_radians(), applied: false, hit_count: 0, - knockback: self.static_data.knockback, + knockback: Knockback::Away(self.static_data.knockback), }); } else if self.timer < self.static_data.swing_duration { if !self.static_data.is_helicopter { diff --git a/common/src/sys/melee.rs b/common/src/sys/melee.rs index 66d2c20b32..35b77e8b61 100644 --- a/common/src/sys/melee.rs +++ b/common/src/sys/melee.rs @@ -150,12 +150,11 @@ impl<'a> System<'a> for Sys { attack.hit_count += 1; } - if attack.knockback != 0.0 && change.amount != 0 { + if change.amount != 0 { let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); server_emitter.emit(ServerEvent::Knockback { entity: b, - impulse: attack.knockback - * *Dir::slerp(kb_dir, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), + impulse: attack.knockback.get_knockback(kb_dir), }); } } diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index 59d8e8cbe2..725919c551 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -8,13 +8,11 @@ use crate::{ span, state::DeltaTime, sync::UidAllocator, - util::Dir, }; use specs::{ saveload::MarkerAllocator, Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage, }; use std::time::Duration; -use vek::*; /// This system is responsible for handling projectile effect triggers pub struct Sys; @@ -125,8 +123,7 @@ impl<'a> System<'a> for Sys { { local_emitter.emit(LocalEvent::ApplyImpulse { entity, - impulse: knockback - * *Dir::slerp(ori.0, Dir::new(Vec3::unit_z()), 0.5), + impulse: knockback.get_knockback(ori.0), }); } }, diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index dbdea44459..35a24691a7 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -212,17 +212,11 @@ impl<'a> System<'a> for Sys { change, }); shockwave_hit_list.hit_entities.push(*uid_b); - } - if shockwave.knockback != 0.0 { let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - let impulse = if shockwave.knockback < 0.0 { - shockwave.knockback - * *Dir::slerp(kb_dir, Dir::new(Vec3::new(0.0, 0.0, -1.0)), 0.85) - } else { - shockwave.knockback - * *Dir::slerp(kb_dir, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5) - }; - server_emitter.emit(ServerEvent::Knockback { entity: b, impulse }); + server_emitter.emit(ServerEvent::Knockback { + entity: b, + impulse: shockwave.knockback.get_knockback(kb_dir), + }); } } } diff --git a/common/src/util/dir.rs b/common/src/util/dir.rs index af9a29ced0..30cb4cfe07 100644 --- a/common/src/util/dir.rs +++ b/common/src/util/dir.rs @@ -98,6 +98,13 @@ impl std::ops::Deref for Dir { impl From<Vec3<f32>> for Dir { fn from(dir: Vec3<f32>) -> Self { Dir::new(dir) } } + +impl std::ops::Neg for Dir { + type Output = Dir; + + fn neg(self) -> Dir { Dir::new(-self.0) } +} + /// Begone ye NaN's /// Slerp two `Vec3`s skipping the slerp if their directions are very close /// This avoids a case where `vek`s slerp produces NaN's From 1a8cf33a60df2da385fb5925a34680c70c81f508 Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Sun, 25 Oct 2020 15:22:40 -0500 Subject: [PATCH 08/10] Transitioned forced movement to an enum. --- common/src/comp/inventory/item/tool.rs | 2 +- common/src/states/combo_melee.rs | 6 ++- common/src/states/dash_melee.rs | 9 +++- common/src/states/leap_melee.rs | 37 +++++-------- common/src/states/repeater_ranged.rs | 26 ++++++--- common/src/states/spin_melee.rs | 9 +++- common/src/states/utils.rs | 73 ++++++++++++++++++++++---- 7 files changed, 115 insertions(+), 47 deletions(-) diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 78d0d40e96..3b4ab95573 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -337,7 +337,7 @@ impl Tool { buildup_duration: Duration::from_millis(200), shoot_duration: Duration::from_millis(200), recover_duration: Duration::from_millis(800), - leap: Some(10.0), + leap: Some(5.0), projectile: Projectile { hit_solid: vec![projectile::Effect::Stick], hit_entity: vec![ diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 4ae0d24efd..ca66efae6e 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -146,11 +146,13 @@ impl CharacterBehavior for Data { StageSection::Swing => { if self.timer < self.static_data.stage_data[stage_index].base_swing_duration { // Forward movement - forward_move( + handle_forced_movement( data, &mut update, + ForcedMovement::Forward { + strength: self.static_data.stage_data[stage_index].forward_movement, + }, 0.3, - self.static_data.stage_data[stage_index].forward_movement, ); // Swings diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index fbd83a2652..820117704e 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -111,7 +111,14 @@ impl CharacterBehavior for Data { && update.energy.current() > 0 { // Forward movement - forward_move(data, &mut update, 0.1, self.static_data.forward_speed); + handle_forced_movement( + data, + &mut update, + ForcedMovement::Forward { + strength: self.static_data.forward_speed, + }, + 0.1, + ); // This logic basically just decides if a charge should end, and prevents the // character state spamming attacks while checking if it has hit something diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index d6b6f297af..0739761281 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -6,7 +6,6 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use std::time::Duration; -use vek::Vec3; /// Separated out to condense update portions of character state #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -76,28 +75,20 @@ impl CharacterBehavior for Data { }, StageSection::Movement => { if self.timer < self.static_data.movement_duration { - // Apply jumping force while in Movement portion of state - update.vel.0 = Vec3::new( - data.inputs.look_dir.x, - data.inputs.look_dir.y, - self.static_data.vertical_leap_strength, - ) * 2.0 - // Multiply decreasing amount linearly over time of - // movement duration - * (1.0 - - self.timer.as_secs_f32() - / self.static_data.movement_duration.as_secs_f32()) - // Apply inputted movement directions at 0.25 strength - + (update.vel.0 * Vec3::new(2.0, 2.0, 0.0) - + 0.25 * data.inputs.move_dir.try_normalized().unwrap_or_default()) - .try_normalized() - .unwrap_or_default() - // Multiply by forward leap strength - * self.static_data.forward_leap_strength - // Control forward movement based on look direction. - // This allows players to stop moving forward when they - // look downward at target - * (1.0 - data.inputs.look_dir.z.abs()); + // Apply jumping force + let progress = 1.0 + - self.timer.as_secs_f32() + / self.static_data.movement_duration.as_secs_f32(); + handle_forced_movement( + data, + &mut update, + ForcedMovement::Leap { + vertical: self.static_data.vertical_leap_strength, + forward: self.static_data.forward_leap_strength, + progress, + }, + 0.15, + ); // Increment duration // If we were to set a timeout for state, this would be diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index c2c4e5d81b..5adfdde10a 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -54,13 +54,18 @@ impl CharacterBehavior for Data { StageSection::Movement => { // Jumping if let Some(leap_strength) = self.static_data.leap { - update.vel.0 = Vec3::new( - data.vel.0.x, - data.vel.0.y, - leap_strength - * (1.0 - - self.timer.as_secs_f32() - / self.static_data.movement_duration.as_secs_f32()), + let progress = 1.0 + - self.timer.as_secs_f32() + / self.static_data.movement_duration.as_secs_f32(); + handle_forced_movement( + data, + &mut update, + ForcedMovement::Leap { + vertical: leap_strength, + forward: 10.0, + progress, + }, + 1.0, ); } if self.timer < self.static_data.movement_duration { @@ -87,7 +92,12 @@ impl CharacterBehavior for Data { StageSection::Buildup => { // Aim gliding if self.static_data.leap.is_some() { - update.vel.0 = Vec3::new(data.vel.0.x, data.vel.0.y, 0.0); + handle_forced_movement( + data, + &mut update, + ForcedMovement::Hover { move_input: 0.1 }, + 1.0, + ); } if self.timer < self.static_data.buildup_duration { // Buildup to attack diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 73765d1607..d685d5dbb4 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -123,7 +123,14 @@ impl CharacterBehavior for Data { }); } else if self.timer < self.static_data.swing_duration { if !self.static_data.is_helicopter { - forward_move(data, &mut update, 0.1, self.static_data.forward_speed); + handle_forced_movement( + data, + &mut update, + ForcedMovement::Forward { + strength: self.static_data.forward_speed, + }, + 0.1, + ); handle_orientation(data, &mut update, 1.0); } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 13e0f50269..b2dd5ecb9e 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -91,18 +91,54 @@ fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { handle_orientation(data, update, data.body.base_ori_rate()); } -/// Similar to basic_move function, but with forced forward movement -pub fn forward_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, forward: f32) { - let accel = if data.physics.on_ground { - data.body.base_accel() - } else { - BASE_HUMANOID_AIR_ACCEL - }; - - update.vel.0 += Vec2::broadcast(data.dt.0) - * accel - * (data.inputs.move_dir * efficiency + (*update.ori.0).xy() * forward); +/// Handles forced movement +pub fn handle_forced_movement( + data: &JoinData, + update: &mut StateUpdate, + movement: ForcedMovement, + efficiency: f32, +) { + match movement { + ForcedMovement::Forward { strength } => { + let accel = if data.physics.on_ground { + data.body.base_accel() + } else { + BASE_HUMANOID_AIR_ACCEL + }; + update.vel.0 += Vec2::broadcast(data.dt.0) + * accel + * (data.inputs.move_dir * efficiency + (*update.ori.0).xy() * strength); + }, + ForcedMovement::Leap { + vertical, + forward, + progress, + } => { + // Apply jumping force + update.vel.0 = Vec3::new( + data.inputs.look_dir.x, + data.inputs.look_dir.y, + vertical, + ) + // Multiply decreasing amount linearly over time (with average of 1) + * 2.0 * progress + // Apply inputted movement directions with some efficiency + + (data.inputs.move_dir.try_normalized().unwrap_or_default() + update.vel.0.xy()) + .try_normalized() + .unwrap_or_default() + // Multiply by forward leap strength + * forward + // Control forward movement based on look direction. + // This allows players to stop moving forward when they + // look downward at target + * (1.0 - data.inputs.look_dir.z.abs()); + }, + ForcedMovement::Hover { move_input } => { + update.vel.0 = Vec3::new(data.vel.0.x, data.vel.0.y, 0.0) + + move_input * data.inputs.move_dir.try_normalized().unwrap_or_default(); + }, + } handle_orientation(data, update, data.body.base_ori_rate() * efficiency); } @@ -389,3 +425,18 @@ pub enum AbilityKey { Skill1, Dodge, } + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub enum ForcedMovement { + Forward { + strength: f32, + }, + Leap { + vertical: f32, + forward: f32, + progress: f32, + }, + Hover { + move_input: f32, + }, +} From 7971034fde457651748feedaccdafd73592fa4b4 Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Tue, 27 Oct 2020 17:04:52 -0500 Subject: [PATCH 09/10] Fixed audio tests. --- .../audio/sfx/event_mapper/combat/tests.rs | 22 +++++++++++++------ .../audio/sfx/event_mapper/movement/tests.rs | 8 ++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index ad03ea0ea5..448c976f6b 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -23,7 +23,10 @@ fn maps_wield_while_equipping() { let result = CombatEventMapper::map_event( &CharacterState::Equipping(states::equipping::Data { - time_left: Duration::from_millis(10), + static_data: states::equipping::StaticData { + buildup_duration: Duration::from_millis(10), + }, + timer: Duration::default(), }), &PreviousEntityState { event: SfxEvent::Idle, @@ -77,12 +80,17 @@ fn maps_basic_melee() { let result = CombatEventMapper::map_event( &CharacterState::BasicMelee(states::basic_melee::Data { - buildup_duration: Duration::default(), - recover_duration: Duration::default(), - knockback: 0.0, - base_healthchange: 10, - range: 1.0, - max_angle: 1.0, + static_data: states::basic_melee::StaticData { + buildup_duration: Duration::default(), + swing_duration: Duration::default(), + recover_duration: Duration::default(), + base_damage: 10, + knockback: 0.0, + range: 1.0, + max_angle: 1.0, + }, + timer: Duration::default(), + stage_section: states::utils::StageSection::Buildup, exhausted: false, }), &PreviousEntityState { diff --git a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs index 40ef0d5edb..1ac43e80a3 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs @@ -153,7 +153,13 @@ fn does_not_map_run_with_sufficient_velocity_but_not_on_ground() { fn maps_roll() { let result = MovementEventMapper::map_movement_event( &CharacterState::Roll(states::roll::Data { - remaining_duration: Duration::from_millis(300), + static_data: states::roll::StaticData { + buildup_duration: Duration::default(), + movement_duration: Duration::default(), + recover_duration: Duration::default(), + }, + timer: Duration::default(), + stage_section: states::utils::StageSection::Buildup, was_wielded: true, }), &PhysicsState { From 52c93f613e60c4f003d7c95b9c742d596717c209 Mon Sep 17 00:00:00 2001 From: Sam <samuelkeiffer@gmail.com> Date: Tue, 27 Oct 2020 17:16:17 -0500 Subject: [PATCH 10/10] Addressed comments. --- common/src/combat.rs | 46 +++++++++------------------- common/src/states/basic_beam.rs | 17 +++------- common/src/states/basic_melee.rs | 21 ++++--------- common/src/states/basic_ranged.rs | 11 +++---- common/src/states/boost.rs | 2 +- common/src/states/charged_melee.rs | 31 +++++-------------- common/src/states/charged_ranged.rs | 21 ++++--------- common/src/states/combo_melee.rs | 29 +++++------------- common/src/states/dash_melee.rs | 46 ++++++---------------------- common/src/states/equipping.rs | 2 +- common/src/states/repeater_ranged.rs | 20 +++++------- common/src/states/roll.rs | 18 +++-------- common/src/states/shockwave.rs | 13 +++----- common/src/states/spin_melee.rs | 30 +++++------------- common/src/sys/beam.rs | 35 +++++++++------------ common/src/sys/melee.rs | 15 ++++----- common/src/sys/projectile.rs | 15 +++++---- common/src/sys/shockwave.rs | 14 ++++----- 18 files changed, 120 insertions(+), 266 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index e80de3abe6..bebda0aa11 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -8,14 +8,22 @@ use vek::*; pub const BLOCK_EFFICIENCY: f32 = 0.9; +/// Each section of this struct determines what damage is applied to a +/// particular target, using some identifier #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Damages { + /// Targets enemies, and all other creatures not in your group pub enemy: Option<Damage>, + /// Targets people in the same group as you, and any pets you have pub group: Option<Damage>, } impl Damages { pub fn new(enemy: Option<Damage>, group: Option<Damage>) -> Self { Damages { enemy, group } } + + pub fn get_damage(self, same_group: bool) -> Option<Damage> { + if same_group { self.group } else { self.enemy } + } } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -49,11 +57,7 @@ impl Damage { damage *= 1.0 - BLOCK_EFFICIENCY } // Armor - let damage_reduction = if let Some(loadout) = loadout { - loadout.get_damage_reduction() - } else { - 0.0 - }; + let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; // Critical damage applies after armor for melee @@ -77,11 +81,7 @@ impl Damage { damage *= 1.0 - BLOCK_EFFICIENCY } // Armor - let damage_reduction = if let Some(loadout) = loadout { - loadout.get_damage_reduction() - } else { - 0.0 - }; + let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; HealthChange { @@ -96,11 +96,7 @@ impl Damage { damage *= 1.0 - BLOCK_EFFICIENCY } // Armor - let damage_reduction = if let Some(loadout) = loadout { - loadout.get_damage_reduction() - } else { - 0.0 - }; + let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; HealthChange { @@ -111,11 +107,7 @@ impl Damage { Damage::Shockwave(damage) => { let mut damage = damage; // Armor - let damage_reduction = if let Some(loadout) = loadout { - loadout.get_damage_reduction() - } else { - 0.0 - }; + let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; HealthChange { @@ -126,11 +118,7 @@ impl Damage { Damage::Energy(damage) => { let mut damage = damage; // Armor - let damage_reduction = if let Some(loadout) = loadout { - loadout.get_damage_reduction() - } else { - 0.0 - }; + let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; HealthChange { @@ -145,11 +133,7 @@ impl Damage { Damage::Falling(damage) => { let mut damage = damage; // Armor - let damage_reduction = if let Some(loadout) = loadout { - loadout.get_damage_reduction() - } else { - 0.0 - }; + let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); if (damage_reduction - 1.0).abs() < f32::EPSILON { damage = 0.0; } @@ -171,7 +155,7 @@ pub enum Knockback { } impl Knockback { - pub fn get_knockback(self, dir: Dir) -> Vec3<f32> { + pub fn calculate_impulse(self, dir: Dir) -> Vec3<f32> { match self { Knockback::Away(strength) => strength * *Dir::slerp(dir, Dir::new(Vec3::unit_z()), 0.5), Knockback::Towards(strength) => { diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 37c84d6264..4a387ba7da 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -74,14 +74,12 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.buildup_duration { // Build up update.character = CharacterState::BasicBeam(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, particle_ori: Some(*data.inputs.look_dir), - offset: self.offset, + ..*self }); } else { // Creates beam @@ -97,11 +95,11 @@ impl CharacterBehavior for Data { }; // Build up update.character = CharacterState::BasicBeam(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Cast, particle_ori: Some(*data.inputs.look_dir), offset: eye_height * 0.55, + ..*self }); } }, @@ -139,14 +137,12 @@ impl CharacterBehavior for Data { ori: Ori(data.inputs.look_dir), }); update.character = CharacterState::BasicBeam(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, particle_ori: Some(*data.inputs.look_dir), - offset: self.offset, + ..*self }); // Consumes energy if there's enough left and ability key is held down @@ -156,25 +152,22 @@ impl CharacterBehavior for Data { ); } else { update.character = CharacterState::BasicBeam(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Recover, particle_ori: Some(*data.inputs.look_dir), - offset: self.offset, + ..*self }); } }, StageSection::Recover => { if self.timer < self.static_data.recover_duration { update.character = CharacterState::BasicBeam(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, particle_ori: Some(*data.inputs.look_dir), - offset: self.offset, + ..*self }); } else { // Done diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 744c30c3b8..228a10a204 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -51,31 +51,27 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.buildup_duration { // Build up update.character = CharacterState::BasicMelee(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Transitions to swing section of stage update.character = CharacterState::BasicMelee(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Swing, - exhausted: self.exhausted, + ..*self }); } }, StageSection::Swing => { if !self.exhausted { update.character = CharacterState::BasicMelee(Data { - static_data: self.static_data, timer: Duration::default(), - stage_section: self.stage_section, exhausted: true, + ..*self }); // Hit attempt @@ -93,21 +89,18 @@ impl CharacterBehavior for Data { } else if self.timer < self.static_data.swing_duration { // Swings update.character = CharacterState::BasicMelee(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Transitions to recover section of stage update.character = CharacterState::BasicMelee(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Recover, - exhausted: self.exhausted, + ..*self }); } }, @@ -115,13 +108,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovery update.character = CharacterState::BasicMelee(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Done diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index bb0950e605..19fff5e2b5 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -54,8 +54,7 @@ impl CharacterBehavior for Data { .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Transitions to recover section of stage @@ -63,7 +62,7 @@ impl CharacterBehavior for Data { static_data: self.static_data.clone(), timer: Duration::default(), stage_section: StageSection::Recover, - exhausted: self.exhausted, + ..*self }); } }, @@ -84,9 +83,8 @@ impl CharacterBehavior for Data { update.character = CharacterState::BasicRanged(Data { static_data: self.static_data.clone(), - timer: self.timer, - stage_section: self.stage_section, exhausted: true, + ..*self }); } else if self.timer < self.static_data.recover_duration { // Recovers @@ -96,8 +94,7 @@ impl CharacterBehavior for Data { .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Done diff --git a/common/src/states/boost.rs b/common/src/states/boost.rs index 564a714c57..4b0110c605 100644 --- a/common/src/states/boost.rs +++ b/common/src/states/boost.rs @@ -36,11 +36,11 @@ impl CharacterBehavior for Data { update.vel.0 += *data.inputs.look_dir * 500.0 * data.dt.0; } update.character = CharacterState::Boost(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), + ..*self }); } else { // Done diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 0da5ada515..e0255e89a4 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -68,14 +68,12 @@ impl CharacterBehavior for Data { // Charge the attack update.character = CharacterState::ChargedMelee(Data { - static_data: self.static_data, - stage_section: self.stage_section, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - exhausted: self.exhausted, charge_amount: charge, + ..*self }); // Consumes energy if there's enough left and RMB is held down @@ -88,14 +86,11 @@ impl CharacterBehavior for Data { { // Maintains charge update.character = CharacterState::ChargedMelee(Data { - static_data: self.static_data, - stage_section: self.stage_section, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - exhausted: self.exhausted, - charge_amount: self.charge_amount, + ..*self }); // Consumes energy if there's enough left and RMB is held down @@ -106,11 +101,9 @@ impl CharacterBehavior for Data { } else { // Transitions to swing update.character = CharacterState::ChargedMelee(Data { - static_data: self.static_data, stage_section: StageSection::Swing, timer: Duration::default(), - exhausted: self.exhausted, - charge_amount: self.charge_amount, + ..*self }); } }, @@ -135,35 +128,28 @@ impl CharacterBehavior for Data { // Starts swinging update.character = CharacterState::ChargedMelee(Data { - static_data: self.static_data, - stage_section: self.stage_section, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), exhausted: true, - charge_amount: self.charge_amount, + ..*self }); } else if self.timer < self.static_data.swing_duration { // Swings update.character = CharacterState::ChargedMelee(Data { - static_data: self.static_data, - stage_section: self.stage_section, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - exhausted: self.exhausted, - charge_amount: self.charge_amount, + ..*self }); } else { // Transitions to recover update.character = CharacterState::ChargedMelee(Data { - static_data: self.static_data, stage_section: StageSection::Recover, timer: Duration::default(), - exhausted: self.exhausted, - charge_amount: self.charge_amount, + ..*self }); } }, @@ -171,14 +157,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovers update.character = CharacterState::ChargedMelee(Data { - static_data: self.static_data, - stage_section: self.stage_section, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - exhausted: self.exhausted, - charge_amount: self.charge_amount, + ..*self }); } else { // Done diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 3b19fa8268..036f9ac5bc 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -63,21 +63,18 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.buildup_duration { // Build up update.character = CharacterState::ChargedRanged(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Transitions to swing section of stage update.character = CharacterState::ChargedRanged(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Charge, - exhausted: self.exhausted, + ..*self }); } }, @@ -124,23 +121,21 @@ impl CharacterBehavior for Data { }); update.character = CharacterState::ChargedRanged(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Recover, exhausted: true, + ..*self }); } else if self.timer < self.static_data.charge_duration && data.inputs.secondary.is_pressed() { // Charges update.character = CharacterState::ChargedRanged(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); // Consumes energy if there's enough left and RMB is held down @@ -151,13 +146,11 @@ impl CharacterBehavior for Data { } else if data.inputs.secondary.is_pressed() { // Holds charge update.character = CharacterState::ChargedRanged(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); // Consumes energy if there's enough left and RMB is held down @@ -171,13 +164,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recovers update.character = CharacterState::ChargedRanged(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Done diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index ca66efae6e..285804e278 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -100,8 +100,6 @@ impl CharacterBehavior for Data { // Build up update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), - stage: self.stage, - combo: self.combo, timer: self .timer .checked_add(Duration::from_secs_f32( @@ -111,18 +109,15 @@ impl CharacterBehavior for Data { * data.dt.0, )) .unwrap_or_default(), - stage_section: self.stage_section, - next_stage: self.next_stage, + ..*self }); } else { // Transitions to swing section of stage update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), - stage: self.stage, - combo: self.combo, timer: Duration::default(), stage_section: StageSection::Swing, - next_stage: self.next_stage, + ..*self }); // Hit attempt @@ -158,8 +153,6 @@ impl CharacterBehavior for Data { // Swings update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), - stage: self.stage, - combo: self.combo, timer: self .timer .checked_add(Duration::from_secs_f32( @@ -169,18 +162,15 @@ impl CharacterBehavior for Data { * data.dt.0, )) .unwrap_or_default(), - stage_section: self.stage_section, - next_stage: self.next_stage, + ..*self }); } else { // Transitions to recover section of stage update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), - stage: self.stage, - combo: self.combo, timer: Duration::default(), stage_section: StageSection::Recover, - next_stage: self.next_stage, + ..*self }); } }, @@ -191,8 +181,6 @@ impl CharacterBehavior for Data { // Checks if state will transition to next stage after recover update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), - stage: self.stage, - combo: self.combo, timer: self .timer .checked_add(Duration::from_secs_f32( @@ -205,14 +193,12 @@ impl CharacterBehavior for Data { * data.dt.0, )) .unwrap_or_default(), - stage_section: self.stage_section, next_stage: true, + ..*self }); } else { update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), - stage: self.stage, - combo: self.combo, timer: self .timer .checked_add(Duration::from_secs_f32( @@ -225,8 +211,7 @@ impl CharacterBehavior for Data { * data.dt.0, )) .unwrap_or_default(), - stage_section: self.stage_section, - next_stage: self.next_stage, + ..*self }); } } else if self.next_stage { @@ -234,10 +219,10 @@ impl CharacterBehavior for Data { update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), stage: (self.stage % self.static_data.num_stages) + 1, - combo: self.combo, timer: Duration::default(), stage_section: StageSection::Buildup, next_stage: false, + ..*self }); } else { // Done diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 820117704e..8a99b80bd9 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -81,25 +81,19 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.buildup_duration { // Build up update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - refresh_timer: self.refresh_timer, - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Transitions to charge section of stage update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, auto_charge: !data.inputs.secondary.is_pressed(), timer: Duration::default(), - refresh_timer: self.refresh_timer, stage_section: StageSection::Charge, - exhausted: self.exhausted, + ..*self }); } }, @@ -146,20 +140,15 @@ impl CharacterBehavior for Data { }); } update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - refresh_timer: self.refresh_timer, - stage_section: self.stage_section, exhausted: true, + ..*self }) } else if self.refresh_timer < Duration::from_millis(50) { update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) @@ -168,20 +157,17 @@ impl CharacterBehavior for Data { .refresh_timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }) } else { update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), refresh_timer: Duration::default(), - stage_section: self.stage_section, exhausted: false, + ..*self }) } @@ -193,12 +179,9 @@ impl CharacterBehavior for Data { } else { // Transitions to swing section of stage update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - auto_charge: self.auto_charge, timer: Duration::default(), - refresh_timer: self.refresh_timer, stage_section: StageSection::Swing, - exhausted: self.exhausted, + ..*self }); } }, @@ -206,25 +189,18 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.swing_duration { // Swings update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - refresh_timer: self.refresh_timer, - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Transitions to recover section of stage update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - auto_charge: self.auto_charge, timer: Duration::default(), - refresh_timer: self.refresh_timer, stage_section: StageSection::Recover, - exhausted: self.exhausted, + ..*self }); } }, @@ -232,15 +208,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recover update.character = CharacterState::DashMelee(Data { - static_data: self.static_data, - auto_charge: self.auto_charge, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - refresh_timer: self.refresh_timer, - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Done diff --git a/common/src/states/equipping.rs b/common/src/states/equipping.rs index b0e63c11ea..fac06e5351 100644 --- a/common/src/states/equipping.rs +++ b/common/src/states/equipping.rs @@ -32,11 +32,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.buildup_duration { // Draw weapon update.character = CharacterState::Equipping(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), + ..*self }); } else { // Done diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index 5adfdde10a..b6ee044680 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -76,8 +76,7 @@ impl CharacterBehavior for Data { .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - reps_remaining: self.reps_remaining, + ..*self }); } else { // Transition to buildup @@ -85,7 +84,7 @@ impl CharacterBehavior for Data { static_data: self.static_data.clone(), timer: Duration::default(), stage_section: StageSection::Buildup, - reps_remaining: self.reps_remaining, + ..*self }); } }, @@ -107,8 +106,7 @@ impl CharacterBehavior for Data { .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - reps_remaining: self.reps_remaining, + ..*self }); } else { // Transition to shoot @@ -116,7 +114,7 @@ impl CharacterBehavior for Data { static_data: self.static_data.clone(), timer: Duration::default(), stage_section: StageSection::Shoot, - reps_remaining: self.reps_remaining, + ..*self }); } }, @@ -162,8 +160,8 @@ impl CharacterBehavior for Data { .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, reps_remaining: self.reps_remaining - 1, + ..*self }); } else if self.timer < self.static_data.shoot_duration { // Finish shooting @@ -173,8 +171,7 @@ impl CharacterBehavior for Data { .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - reps_remaining: self.reps_remaining, + ..*self }); } else { // Transition to recover @@ -182,7 +179,7 @@ impl CharacterBehavior for Data { static_data: self.static_data.clone(), timer: Duration::default(), stage_section: StageSection::Recover, - reps_remaining: self.reps_remaining, + ..*self }); } }, @@ -198,8 +195,7 @@ impl CharacterBehavior for Data { .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - reps_remaining: self.reps_remaining, + ..*self }); } else { // Done diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index 2a49f44445..cb18a441f5 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -54,21 +54,18 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.buildup_duration { // Build up update.character = CharacterState::Roll(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - was_wielded: self.was_wielded, + ..*self }); } else { // Transitions to movement section of stage update.character = CharacterState::Roll(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Movement, - was_wielded: self.was_wielded, + ..*self }); } }, @@ -76,21 +73,18 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.movement_duration { // Movement update.character = CharacterState::Roll(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - was_wielded: self.was_wielded, + ..*self }); } else { // Transitions to recover section of stage update.character = CharacterState::Roll(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Recover, - was_wielded: self.was_wielded, + ..*self }); } }, @@ -98,13 +92,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Build up update.character = CharacterState::Roll(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, - was_wielded: self.was_wielded, + ..*self }); } else { // Done diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index a9bb69cd07..de0cbf3ab5 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -57,12 +57,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.buildup_duration { // Build up update.character = CharacterState::Shockwave(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, + ..*self }); } else { // Attack @@ -87,9 +86,9 @@ impl CharacterBehavior for Data { // Transitions to swing update.character = CharacterState::Shockwave(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Swing, + ..*self }); } }, @@ -97,19 +96,18 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.swing_duration { // Swings update.character = CharacterState::Shockwave(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, + ..*self }); } else { // Transitions to recover update.character = CharacterState::Shockwave(Data { - static_data: self.static_data, timer: Duration::default(), stage_section: StageSection::Recover, + ..*self }); } }, @@ -117,12 +115,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.swing_duration { // Recovers update.character = CharacterState::Shockwave(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - stage_section: self.stage_section, + ..*self }); } else { // Done diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index d685d5dbb4..14c4bec054 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -80,34 +80,27 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.buildup_duration { // Build up update.character = CharacterState::SpinMelee(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - spins_remaining: self.spins_remaining, - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Transitions to swing section of stage update.character = CharacterState::SpinMelee(Data { - static_data: self.static_data, timer: Duration::default(), - spins_remaining: self.spins_remaining, stage_section: StageSection::Swing, - exhausted: self.exhausted, + ..*self }); } }, StageSection::Swing => { if !self.exhausted { update.character = CharacterState::SpinMelee(Data { - static_data: self.static_data, timer: Duration::default(), - spins_remaining: self.spins_remaining, - stage_section: self.stage_section, exhausted: true, + ..*self }); // Hit attempt data.updater.insert(data.entity, Attacking { @@ -136,14 +129,11 @@ impl CharacterBehavior for Data { // Swings update.character = CharacterState::SpinMelee(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - spins_remaining: self.spins_remaining, - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else if update.energy.current() >= self.static_data.energy_cost && (self.spins_remaining != 0 @@ -155,11 +145,10 @@ impl CharacterBehavior for Data { self.spins_remaining - 1 }; update.character = CharacterState::SpinMelee(Data { - static_data: self.static_data, timer: Duration::default(), spins_remaining: new_spins_remaining, - stage_section: self.stage_section, exhausted: false, + ..*self }); // Consumes energy if there's enough left and RMB is held down update.energy.change_by( @@ -169,11 +158,9 @@ impl CharacterBehavior for Data { } else { // Transitions to recover section of stage update.character = CharacterState::SpinMelee(Data { - static_data: self.static_data, timer: Duration::default(), - spins_remaining: self.spins_remaining, stage_section: StageSection::Recover, - exhausted: self.exhausted, + ..*self }); } }, @@ -181,14 +168,11 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.recover_duration { // Recover update.character = CharacterState::SpinMelee(Data { - static_data: self.static_data, timer: self .timer .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - spins_remaining: self.spins_remaining, - stage_section: self.stage_section, - exhausted: self.exhausted, + ..*self }); } else { // Done diff --git a/common/src/sys/beam.rs b/common/src/sys/beam.rs index e6abc2ecf0..ea6f5dd3b2 100644 --- a/common/src/sys/beam.rs +++ b/common/src/sys/beam.rs @@ -168,10 +168,8 @@ impl<'a> System<'a> for Sys { continue; } - let damage = if !same_group && beam_segment.damages.enemy.is_some() { - beam_segment.damages.enemy.unwrap() - } else if same_group && beam_segment.damages.group.is_some() { - beam_segment.damages.group.unwrap() + let damage = if let Some(damage) = beam_segment.damages.get_damage(same_group) { + damage } else { continue; }; @@ -182,7 +180,7 @@ impl<'a> System<'a> for Sys { let change = damage.modify_damage(block, loadouts.get(b), beam_segment.owner); - if let Damage::Energy(_) = damage { + if matches!(damage, Damage::Healing(_)) { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, change, @@ -205,21 +203,18 @@ impl<'a> System<'a> for Sys { EnergySource::HitEnemy, ); } - } - if let Damage::Healing(_) = damage { - if let Some(energy_mut) = beam_owner.and_then(|o| energies.get_mut(o)) { - if energy_mut - .try_change_by( - -(beam_segment.energy_cost as i32), // Stamina use - EnergySource::Ability, - ) - .is_ok() - { - server_emitter.emit(ServerEvent::Damage { - uid: *uid_b, - change, - }); - } + } else if let Some(energy_mut) = beam_owner.and_then(|o| energies.get_mut(o)) { + if energy_mut + .try_change_by( + -(beam_segment.energy_cost as i32), // Stamina use + EnergySource::Ability, + ) + .is_ok() + { + server_emitter.emit(ServerEvent::Damage { + uid: *uid_b, + change, + }); } } // Adds entities that were hit to the hit_entities list on the beam, sees if it diff --git a/common/src/sys/melee.rs b/common/src/sys/melee.rs index 35b77e8b61..b974ca6bd4 100644 --- a/common/src/sys/melee.rs +++ b/common/src/sys/melee.rs @@ -11,7 +11,6 @@ use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage} use std::time::Duration; use vek::*; -pub const BLOCK_EFFICIENCY: f32 = 0.9; pub const BLOCK_ANGLE: f32 = 180.0; /// This system is responsible for handling accepted inputs like moving or @@ -111,10 +110,8 @@ impl<'a> System<'a> for Sys { .map(|group_a| Some(group_a) == groups.get(b)) .unwrap_or(false); - let damage = if !same_group && attack.damages.enemy.is_some() { - attack.damages.enemy.unwrap() - } else if same_group && attack.damages.group.is_some() { - attack.damages.group.unwrap() + let damage = if let Some(damage) = attack.damages.get_damage(same_group) { + damage } else { continue; }; @@ -152,10 +149,10 @@ impl<'a> System<'a> for Sys { if change.amount != 0 { let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - server_emitter.emit(ServerEvent::Knockback { - entity: b, - impulse: attack.knockback.get_knockback(kb_dir), - }); + let impulse = attack.knockback.calculate_impulse(kb_dir); + if !impulse.is_approx_zero() { + server_emitter.emit(ServerEvent::Knockback { entity: b, impulse }); + } } } } diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index 725919c551..cfae192ec9 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -100,10 +100,8 @@ impl<'a> System<'a> for Sys { if Some(other) == projectile.owner { continue; } - let damage = if !same_group && damages.enemy.is_some() { - damages.enemy.unwrap() - } else if same_group && damages.group.is_some() { - damages.group.unwrap() + let damage = if let Some(damage) = damages.get_damage(same_group) { + damage } else { continue; }; @@ -121,10 +119,11 @@ impl<'a> System<'a> for Sys { if let Some(entity) = uid_allocator.retrieve_entity_internal(other.into()) { - local_emitter.emit(LocalEvent::ApplyImpulse { - entity, - impulse: knockback.get_knockback(ori.0), - }); + let impulse = knockback.calculate_impulse(ori.0); + if !impulse.is_approx_zero() { + local_emitter + .emit(LocalEvent::ApplyImpulse { entity, impulse }); + } } }, projectile::Effect::RewardEnergy(energy) => { diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index 35a24691a7..0da87146bf 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -192,10 +192,8 @@ impl<'a> System<'a> for Sys { && (!shockwave.requires_ground || physics_state_b.on_ground); if hit { - let damage = if !same_group && shockwave.damages.enemy.is_some() { - shockwave.damages.enemy.unwrap() - } else if same_group && shockwave.damages.group.is_some() { - shockwave.damages.group.unwrap() + let damage = if let Some(damage) = shockwave.damages.get_damage(same_group) { + damage } else { continue; }; @@ -213,10 +211,10 @@ impl<'a> System<'a> for Sys { }); shockwave_hit_list.hit_entities.push(*uid_b); let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0)); - server_emitter.emit(ServerEvent::Knockback { - entity: b, - impulse: shockwave.knockback.get_knockback(kb_dir), - }); + let impulse = shockwave.knockback.calculate_impulse(kb_dir); + if !impulse.is_approx_zero() { + server_emitter.emit(ServerEvent::Knockback { entity: b, impulse }); + } } } }