From c83296a4ec88996a58143907c0256366be565ef2 Mon Sep 17 00:00:00 2001 From: jiminycrick Date: Sat, 5 Dec 2020 10:23:45 -0800 Subject: [PATCH] Initial broken implementation of poise Character movement no longer broken. --- 4 | 286 ++++++++++++++++++ assets/common/abilities/axe/doublestrike.ron | 6 +- assets/common/abilities/axe/leap.ron | 3 +- assets/common/abilities/axe/spin.ron | 3 +- assets/common/abilities/bow/charged.ron | 1 + assets/common/abilities/dagger/tempbasic.ron | 3 +- assets/common/abilities/empty/basic.ron | 3 +- assets/common/abilities/farming/basic.ron | 3 +- assets/common/abilities/hammer/charged.ron | 1 + assets/common/abilities/hammer/leap.ron | 3 +- .../common/abilities/hammer/singlestrike.ron | 5 +- assets/common/abilities/shield/tempbasic.ron | 3 +- .../common/abilities/staff/fireshockwave.ron | 3 +- assets/common/abilities/sword/dash.ron | 1 + assets/common/abilities/sword/spin.ron | 3 +- .../common/abilities/sword/triplestrike.ron | 5 +- .../abilities/unique/beastclaws/basic.ron | 3 +- .../unique/quadlowbasic/singlestrike.ron | 3 +- .../unique/quadlowbasic/triplestrike.ron | 5 +- .../abilities/unique/quadlowbreathe/dash.ron | 1 + .../unique/quadlowbreathe/triplestrike.ron | 3 + .../abilities/unique/quadlowquick/dash.ron | 1 + .../unique/quadlowquick/quadstrike.ron | 6 +- .../unique/quadlowranged/singlestrike.ron | 3 +- .../abilities/unique/quadlowtail/charged.ron | 1 + .../unique/quadlowtail/triplestrike.ron | 5 +- .../unique/quadmedbasic/singlestrike.ron | 1 + .../unique/quadmedbasic/triplestrike.ron | 3 + .../abilities/unique/quadmedcharge/dash.ron | 1 + .../unique/quadmedcharge/doublestrike.ron | 2 + .../abilities/unique/quadmedhoof/basic.ron | 3 +- .../unique/quadmedjump/doublestrike.ron | 2 + .../abilities/unique/quadmedjump/leap.ron | 1 + .../unique/quadmedjump/quickleap.ron | 1 + .../abilities/unique/quadmedquick/dash.ron | 1 + .../unique/quadmedquick/triplestrike.ron | 3 + .../unique/quadsmallbasic/singlestrike.ron | 3 +- .../abilities/unique/stonegolemfist/basic.ron | 3 +- .../unique/stonegolemfist/shockwave.ron | 3 +- .../unique/theropodbasic/singlestrike.ron | 3 +- .../unique/theropodbasic/triplestrike.ron | 3 + .../unique/theropodbird/singlestrike.ron | 3 +- .../unique/theropodbird/triplestrike.ron | 3 + common/net/src/msg/ecs_packet.rs | 5 + common/src/combat.rs | 103 ++++--- common/src/comp/ability.rs | 22 ++ common/src/comp/body.rs | 15 + common/src/comp/character_state.rs | 8 + common/src/comp/mod.rs | 2 + common/src/comp/poise.rs | 106 +++++++ common/src/comp/projectile.rs | 7 + common/src/effect.rs | 5 + common/src/event.rs | 3 +- common/src/states/basic_beam.rs | 2 + common/src/states/basic_melee.rs | 3 + common/src/states/behavior.rs | 13 +- common/src/states/charged_melee.rs | 3 + common/src/states/charged_ranged.rs | 3 + common/src/states/combo_melee.rs | 5 + common/src/states/dash_melee.rs | 3 + common/src/states/leap_melee.rs | 3 + common/src/states/mod.rs | 2 + common/src/states/shockwave.rs | 3 + common/src/states/spin_melee.rs | 3 + common/src/states/staggered.rs | 89 ++++++ common/src/states/stunned.rs | 89 ++++++ common/sys/src/beam.rs | 17 +- common/sys/src/buff.rs | 2 +- common/sys/src/character_behavior.rs | 9 +- common/sys/src/melee.rs | 4 +- common/sys/src/state.rs | 1 + common/sys/src/stats.rs | 5 +- server/src/cmd.rs | 5 +- server/src/events/entity_creation.rs | 5 +- server/src/events/entity_manipulation.rs | 83 ++++- server/src/events/mod.rs | 2 + server/src/rtsim/tick.rs | 1 + server/src/state_ext.rs | 10 + server/src/sys/object.rs | 2 + server/src/sys/sentinel.rs | 9 +- server/src/sys/terrain.rs | 2 + 81 files changed, 965 insertions(+), 91 deletions(-) create mode 100644 4 create mode 100644 common/src/comp/poise.rs create mode 100644 common/src/states/staggered.rs create mode 100644 common/src/states/stunned.rs diff --git a/4 b/4 new file mode 100644 index 0000000000..628b6390ea --- /dev/null +++ b/4 @@ -0,0 +1,286 @@ +use crate::{ + comp::{Attacking, CharacterState, EnergyChange, EnergySource, StateUpdate}, + states::{ + behavior::{CharacterBehavior, JoinData}, + utils::*, + }, + Damage, DamageSource, GroupTarget, Knockback, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub struct Stage { + /// Specifies which stage the combo attack is in + pub stage: u32, + /// Initial damage of stage + pub base_damage: u32, + /// Max damage of stage + pub max_damage: u32, + /// Damage scaling per combo + pub damage_increase: u32, + /// Initial poise damage of stage + pub base_poise_damage: u32, + /// Knockback of stage + pub knockback: f32, + /// Range of attack + pub range: f32, + /// Angle of attack + pub angle: f32, + /// Initial buildup duration of stage (how long until state can deal damage) + pub base_buildup_duration: T, + /// Duration of stage spent in swing (controls animation stuff, and can also + /// be used to handle movement separately to buildup) + pub base_swing_duration: T, + /// Initial recover duration of stage (how long until character exits state) + pub base_recover_duration: T, + /// How much forward movement there is in the swing portion of the stage + pub forward_movement: f32, +} + +impl Stage { + pub fn to_duration(self) -> Stage { + Stage:: { + stage: self.stage, + base_damage: self.base_damage, + max_damage: self.max_damage, + damage_increase: self.damage_increase, + base_poise_damage: self.base_poise_damage, + knockback: self.knockback, + range: self.range, + angle: self.angle, + base_buildup_duration: Duration::from_millis(self.base_buildup_duration), + base_swing_duration: Duration::from_millis(self.base_swing_duration), + base_recover_duration: Duration::from_millis(self.base_recover_duration), + forward_movement: self.forward_movement, + } + } + + pub fn adjusted_by_stats(mut self, power: f32, speed: f32) -> Self { + self.base_damage = (self.base_damage as f32 * power) as u32; + self.max_damage = (self.max_damage as f32 * power) as u32; + self.damage_increase = (self.damage_increase as f32 * power) as u32; + self.base_buildup_duration = (self.base_buildup_duration as f32 / speed) as u64; + self.base_swing_duration = (self.base_swing_duration as f32 / speed) as u64; + self.base_recover_duration = (self.base_recover_duration as f32 / speed) as u64; + self + } +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +/// Separated out to condense update portions of character state +pub struct StaticData { + /// Indicates number of stages in combo + pub num_stages: u32, + /// Data for each stage + pub stage_data: Vec>, + /// Initial energy gain per strike + pub initial_energy_gain: u32, + /// Max energy gain per strike + pub max_energy_gain: u32, + /// Energy gain increase per combo + pub energy_increase: u32, + /// (100% - speed_increase) is percentage speed increases from current to + /// max when combo increases + pub speed_increase: f32, + /// (100% + max_speed_increase) is the max attack speed + pub max_speed_increase: f32, + /// Whether the state can be interrupted by other abilities + pub is_interruptible: bool, + /// What key is used to press ability + pub ability_key: AbilityKey, +} +/// A sequence of attacks that can incrementally become faster and more +/// damaging. +#[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, + /// Indicates what stage the combo is in + pub stage: u32, + /// Number of consecutive strikes + pub combo: u32, + /// Timer for each stage + pub timer: Duration, + /// Checks what section a stage is in + pub stage_section: StageSection, + /// Whether the state should go onto the next stage + pub next_stage: bool, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + handle_orientation(data, &mut update, 1.0); + handle_move(data, &mut update, 0.3); + if !ability_key_is_pressed(data, self.static_data.ability_key) { + handle_interrupt(data, &mut update, self.static_data.is_interruptible); + if let CharacterState::Roll(roll) = &mut update.character { + roll.was_combo = Some((self.stage, self.combo)); + } + match update.character { + CharacterState::ComboMelee(_) => {}, + _ => { + return update; + }, + } + } + + let stage_index = (self.stage - 1) as usize; + + let speed_modifer = 1.0 + + self.static_data.max_speed_increase + * (1.0 - self.static_data.speed_increase.powi(self.combo as i32)); + + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.stage_data[stage_index].base_buildup_duration { + // Build up + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0 * speed_modifer)) + .unwrap_or_default(), + ..*self + }); + } else { + // Transitions to swing section of stage + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + timer: Duration::default(), + stage_section: StageSection::Swing, + ..*self + }); + + // 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, + ); + let poise_damage = self.static_data.stage_data[stage_index].base_poise_damage; + data.updater.insert(data.entity, Attacking { + damages: vec![(Some(GroupTarget::OutOfGroup), Damage { + source: DamageSource::Melee, + value: damage as f32, + poise_damage: poise_damage as f32, + })], + range: self.static_data.stage_data[stage_index].range, + max_angle: self.static_data.stage_data[stage_index].angle.to_radians(), + applied: false, + hit_count: 0, + knockback: Knockback::Away( + self.static_data.stage_data[stage_index].knockback, + ), + }); + } + }, + StageSection::Swing => { + if self.timer < self.static_data.stage_data[stage_index].base_swing_duration { + // Forward movement + handle_forced_movement( + data, + &mut update, + ForcedMovement::Forward { + strength: self.static_data.stage_data[stage_index].forward_movement, + }, + 0.3, + ); + + // Swings + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0 * speed_modifer)) + .unwrap_or_default(), + ..*self + }); + } else { + // Transitions to recover section of stage + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.stage_data[stage_index].base_recover_duration { + // Recovers + if ability_key_is_pressed(data, self.static_data.ability_key) { + // Checks if state will transition to next stage after recover + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0 * speed_modifer)) + .unwrap_or_default(), + next_stage: true, + ..*self + }); + } else { + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0 * speed_modifer)) + .unwrap_or_default(), + ..*self + }); + } + } else if self.next_stage { + // Transitions to buildup section of next stage + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + stage: (self.stage % self.static_data.num_stages) + 1, + timer: Duration::default(), + stage_section: StageSection::Buildup, + next_stage: false, + ..*self + }); + } else { + // Done + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::(data.entity); + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::(data.entity); + }, + } + + // Grant energy on successful hit + if let Some(attack) = data.attacking { + if attack.applied && attack.hit_count > 0 { + let energy = self.static_data.max_energy_gain.min( + self.static_data.initial_energy_gain + + self.combo * self.static_data.energy_increase, + ) as i32; + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + stage: self.stage, + combo: self.combo + 1, + timer: self.timer, + stage_section: self.stage_section, + next_stage: self.next_stage, + }); + data.updater.remove::(data.entity); + update.energy.change_by(EnergyChange { + amount: energy, + source: EnergySource::HitEnemy, + }); + } + } + + update + } +} diff --git a/assets/common/abilities/axe/doublestrike.ron b/assets/common/abilities/axe/doublestrike.ron index cf52d4639f..7a1d0111b9 100644 --- a/assets/common/abilities/axe/doublestrike.ron +++ b/assets/common/abilities/axe/doublestrike.ron @@ -3,6 +3,8 @@ ComboMelee( ( stage: 1, base_damage: 90, + max_damage: 110, + base_poise_damage: 40, damage_increase: 10, knockback: 8.0, range: 3.5, @@ -15,6 +17,8 @@ ComboMelee( ( stage: 2, base_damage: 130, + max_damage: 160, + base_poise_damage: 40, damage_increase: 15, knockback: 12.0, range: 3.5, @@ -32,4 +36,4 @@ ComboMelee( max_speed_increase: 0.6, scales_from_combo: 2, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/axe/leap.ron b/assets/common/abilities/axe/leap.ron index 1b6cd34c6d..ed8d37c32e 100644 --- a/assets/common/abilities/axe/leap.ron +++ b/assets/common/abilities/axe/leap.ron @@ -5,9 +5,10 @@ LeapMelee( swing_duration: 200, recover_duration: 200, base_damage: 240, + base_poise_damage: 70, knockback: 12.0, range: 4.5, max_angle: 30.0, forward_leap_strength: 28.0, vertical_leap_strength: 8.0, -) \ No newline at end of file +) diff --git a/assets/common/abilities/axe/spin.ron b/assets/common/abilities/axe/spin.ron index a26b860d02..e70a6acb9e 100644 --- a/assets/common/abilities/axe/spin.ron +++ b/assets/common/abilities/axe/spin.ron @@ -3,6 +3,7 @@ SpinMelee( swing_duration: 400, recover_duration: 200, base_damage: 60, + base_poise_damage: 20, knockback: 0.0, range: 3.5, energy_cost: 100, @@ -11,4 +12,4 @@ SpinMelee( is_interruptible: false, forward_speed: 0.0, num_spins: 1, -) \ No newline at end of file +) diff --git a/assets/common/abilities/bow/charged.ron b/assets/common/abilities/bow/charged.ron index cb098807d1..2a20c768b8 100644 --- a/assets/common/abilities/bow/charged.ron +++ b/assets/common/abilities/bow/charged.ron @@ -3,6 +3,7 @@ ChargedRanged( energy_drain: 300, initial_damage: 10, scaled_damage: 190, + initial_poise_damage: 10, initial_knockback: 10.0, scaled_knockback: 10.0, speed: 1.0, diff --git a/assets/common/abilities/dagger/tempbasic.ron b/assets/common/abilities/dagger/tempbasic.ron index 0d31c0b0f4..c42e13bbd2 100644 --- a/assets/common/abilities/dagger/tempbasic.ron +++ b/assets/common/abilities/dagger/tempbasic.ron @@ -4,7 +4,8 @@ BasicMelee( swing_duration: 100, recover_duration: 300, base_damage: 50, + base_poise_damage: 10, knockback: 0.0, range: 3.5, max_angle: 20.0, -) \ No newline at end of file +) diff --git a/assets/common/abilities/empty/basic.ron b/assets/common/abilities/empty/basic.ron index 3562af81b5..8d2fdf9aee 100644 --- a/assets/common/abilities/empty/basic.ron +++ b/assets/common/abilities/empty/basic.ron @@ -4,7 +4,8 @@ BasicMelee( swing_duration: 100, recover_duration: 900, base_damage: 20, + base_poise_damage: 5, knockback: 0.0, range: 3.5, max_angle: 15.0, -) \ No newline at end of file +) diff --git a/assets/common/abilities/farming/basic.ron b/assets/common/abilities/farming/basic.ron index 0a60b62df7..dda81a894a 100644 --- a/assets/common/abilities/farming/basic.ron +++ b/assets/common/abilities/farming/basic.ron @@ -4,7 +4,8 @@ BasicMelee( swing_duration: 100, recover_duration: 150, base_damage: 50, + base_poise_damage: 5, knockback: 0.0, range: 3.5, max_angle: 20.0, -) \ No newline at end of file +) diff --git a/assets/common/abilities/hammer/charged.ron b/assets/common/abilities/hammer/charged.ron index 916ad9ce4e..bde38d4c43 100644 --- a/assets/common/abilities/hammer/charged.ron +++ b/assets/common/abilities/hammer/charged.ron @@ -3,6 +3,7 @@ ChargedMelee( energy_drain: 300, initial_damage: 10, scaled_damage: 160, + initial_poise_damage: 10, initial_knockback: 10.0, scaled_knockback: 50.0, range: 3.5, diff --git a/assets/common/abilities/hammer/leap.ron b/assets/common/abilities/hammer/leap.ron index 70ec3019bf..1e7a1faad7 100644 --- a/assets/common/abilities/hammer/leap.ron +++ b/assets/common/abilities/hammer/leap.ron @@ -5,9 +5,10 @@ LeapMelee( swing_duration: 150, recover_duration: 200, base_damage: 240, + base_poise_damage: 60, knockback: 25.0, range: 4.5, max_angle: 360.0, forward_leap_strength: 28.0, vertical_leap_strength: 8.0, -) \ No newline at end of file +) diff --git a/assets/common/abilities/hammer/singlestrike.ron b/assets/common/abilities/hammer/singlestrike.ron index 554f30c73c..bd6ea6ff69 100644 --- a/assets/common/abilities/hammer/singlestrike.ron +++ b/assets/common/abilities/hammer/singlestrike.ron @@ -3,7 +3,8 @@ ComboMelee( stage: 1, base_damage: 130, damage_increase: 10, - knockback: 10.0, + base_poise_damage: 30, + knockback: 0.0, range: 4.5, angle: 50.0, base_buildup_duration: 600, @@ -18,4 +19,4 @@ ComboMelee( max_speed_increase: 0.4, scales_from_combo: 2, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/shield/tempbasic.ron b/assets/common/abilities/shield/tempbasic.ron index eca1996eb1..3c823cea25 100644 --- a/assets/common/abilities/shield/tempbasic.ron +++ b/assets/common/abilities/shield/tempbasic.ron @@ -4,7 +4,8 @@ BasicMelee( swing_duration: 100, recover_duration: 300, base_damage: 40, + base_poise_damage: 20, knockback: 0.0, range: 3.0, max_angle: 120.0, -) \ No newline at end of file +) diff --git a/assets/common/abilities/staff/fireshockwave.ron b/assets/common/abilities/staff/fireshockwave.ron index fabf1b00b5..a45a280454 100644 --- a/assets/common/abilities/staff/fireshockwave.ron +++ b/assets/common/abilities/staff/fireshockwave.ron @@ -4,6 +4,7 @@ Shockwave( swing_duration: 100, recover_duration: 300, damage: 200, + poise_damage: 10, knockback: Away(25.0), shockwave_angle: 360.0, shockwave_vertical_angle: 90.0, @@ -11,4 +12,4 @@ Shockwave( shockwave_duration: 500, requires_ground: false, move_efficiency: 0.1, -) \ No newline at end of file +) diff --git a/assets/common/abilities/sword/dash.ron b/assets/common/abilities/sword/dash.ron index 6ca2de60b7..2160ed162a 100644 --- a/assets/common/abilities/sword/dash.ron +++ b/assets/common/abilities/sword/dash.ron @@ -2,6 +2,7 @@ DashMelee( energy_cost: 100, base_damage: 80, scaled_damage: 160, + base_poise_damage: 20, base_knockback: 8.0, scaled_knockback: 7.0, range: 5.0, diff --git a/assets/common/abilities/sword/spin.ron b/assets/common/abilities/sword/spin.ron index 64edbfb839..97d685f085 100644 --- a/assets/common/abilities/sword/spin.ron +++ b/assets/common/abilities/sword/spin.ron @@ -3,6 +3,7 @@ SpinMelee( swing_duration: 400, recover_duration: 500, base_damage: 160, + base_poise_damage: 10, knockback: 10.0, range: 3.5, energy_cost: 150, @@ -11,4 +12,4 @@ SpinMelee( is_interruptible: true, forward_speed: 1.0, num_spins: 3, -) \ No newline at end of file +) diff --git a/assets/common/abilities/sword/triplestrike.ron b/assets/common/abilities/sword/triplestrike.ron index ab19e0fd43..647eb2e2c6 100644 --- a/assets/common/abilities/sword/triplestrike.ron +++ b/assets/common/abilities/sword/triplestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 100, damage_increase: 10, + base_poise_damage: 10, knockback: 10.0, range: 4.0, angle: 30.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 80, damage_increase: 15, + base_poise_damage: 10, knockback: 12.0, range: 3.5, angle: 180.0, @@ -28,6 +30,7 @@ ComboMelee( stage: 3, base_damage: 130, damage_increase: 20, + base_poise_damage: 10, knockback: 14.0, range: 6.0, angle: 10.0, @@ -44,4 +47,4 @@ ComboMelee( max_speed_increase: 0.8, scales_from_combo: 2, is_interruptible: true, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/beastclaws/basic.ron b/assets/common/abilities/unique/beastclaws/basic.ron index 77e860349c..26899baacc 100644 --- a/assets/common/abilities/unique/beastclaws/basic.ron +++ b/assets/common/abilities/unique/beastclaws/basic.ron @@ -5,6 +5,7 @@ BasicMelee( recover_duration: 250, knockback: 25.0, base_damage: 200, + base_poise_damage: 30, range: 5.0, max_angle: 120.0, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/quadlowbasic/singlestrike.ron b/assets/common/abilities/unique/quadlowbasic/singlestrike.ron index dc6c1cd6b2..1f82c53760 100644 --- a/assets/common/abilities/unique/quadlowbasic/singlestrike.ron +++ b/assets/common/abilities/unique/quadlowbasic/singlestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 100, damage_increase: 0, + base_poise_damage: 30, knockback: 5.0, range: 3.5, angle: 60.0, @@ -20,4 +21,4 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/quadlowbasic/triplestrike.ron b/assets/common/abilities/unique/quadlowbasic/triplestrike.ron index 94beb996c4..ec21c62dfa 100644 --- a/assets/common/abilities/unique/quadlowbasic/triplestrike.ron +++ b/assets/common/abilities/unique/quadlowbasic/triplestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 120, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 80, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -28,6 +30,7 @@ ComboMelee( stage: 3, base_damage: 130, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -44,4 +47,4 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/quadlowbreathe/dash.ron b/assets/common/abilities/unique/quadlowbreathe/dash.ron index e5081de2ac..3f12815a4a 100644 --- a/assets/common/abilities/unique/quadlowbreathe/dash.ron +++ b/assets/common/abilities/unique/quadlowbreathe/dash.ron @@ -2,6 +2,7 @@ DashMelee( energy_cost: 0, base_damage: 150, scaled_damage: 110, + base_poise_damage: 60, base_knockback: 8.0, scaled_knockback: 17.0, range: 5.0, diff --git a/assets/common/abilities/unique/quadlowbreathe/triplestrike.ron b/assets/common/abilities/unique/quadlowbreathe/triplestrike.ron index dd78e2893c..ee2db98e39 100644 --- a/assets/common/abilities/unique/quadlowbreathe/triplestrike.ron +++ b/assets/common/abilities/unique/quadlowbreathe/triplestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 100, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 4.5, angle: 30.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 80, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -28,6 +30,7 @@ ComboMelee( stage: 3, base_damage: 130, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, diff --git a/assets/common/abilities/unique/quadlowquick/dash.ron b/assets/common/abilities/unique/quadlowquick/dash.ron index 21b070b83e..79bdaaeef9 100644 --- a/assets/common/abilities/unique/quadlowquick/dash.ron +++ b/assets/common/abilities/unique/quadlowquick/dash.ron @@ -2,6 +2,7 @@ DashMelee( energy_cost: 0, base_damage: 30, scaled_damage: 10, + base_poise_damage: 30, base_knockback: 8.0, scaled_knockback: 7.0, range: 2.0, diff --git a/assets/common/abilities/unique/quadlowquick/quadstrike.ron b/assets/common/abilities/unique/quadlowquick/quadstrike.ron index 36700b4d3a..7dc4c26281 100644 --- a/assets/common/abilities/unique/quadlowquick/quadstrike.ron +++ b/assets/common/abilities/unique/quadlowquick/quadstrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 100, damage_increase: 0, + base_poise_damage: 10, knockback: 2.0, range: 3.5, angle: 30.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 130, damage_increase: 0, + base_poise_damage: 10, knockback: 2.0, range: 3.5, angle: 30.0, @@ -28,6 +30,7 @@ ComboMelee( stage: 3, base_damage: 130, damage_increase: 0, + base_poise_damage: 10, knockback: 2.0, range: 3.5, angle: 30.0, @@ -40,6 +43,7 @@ ComboMelee( stage: 4, base_damage: 130, damage_increase: 0, + base_poise_damage: 10, knockback: 8.0, range: 3.5, angle: 30.0, @@ -56,4 +60,4 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/quadlowranged/singlestrike.ron b/assets/common/abilities/unique/quadlowranged/singlestrike.ron index bd24b84ae8..604ef7f9f8 100644 --- a/assets/common/abilities/unique/quadlowranged/singlestrike.ron +++ b/assets/common/abilities/unique/quadlowranged/singlestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 60, damage_increase: 0, + base_poise_damage: 30, knockback: 5.0, range: 3.5, angle: 60.0, @@ -20,4 +21,4 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/quadlowtail/charged.ron b/assets/common/abilities/unique/quadlowtail/charged.ron index a5f1890962..7057a6cf2f 100644 --- a/assets/common/abilities/unique/quadlowtail/charged.ron +++ b/assets/common/abilities/unique/quadlowtail/charged.ron @@ -3,6 +3,7 @@ ChargedMelee( energy_drain: 0, initial_damage: 160, scaled_damage: 40, + initial_poise_damage: 50, initial_knockback: 10.0, scaled_knockback: 20.0, range: 6.0, diff --git a/assets/common/abilities/unique/quadlowtail/triplestrike.ron b/assets/common/abilities/unique/quadlowtail/triplestrike.ron index 14b1f9bb86..d7e08a9f61 100644 --- a/assets/common/abilities/unique/quadlowtail/triplestrike.ron +++ b/assets/common/abilities/unique/quadlowtail/triplestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 100, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 120, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -28,6 +30,7 @@ ComboMelee( stage: 3, base_damage: 130, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -44,4 +47,4 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/quadmedbasic/singlestrike.ron b/assets/common/abilities/unique/quadmedbasic/singlestrike.ron index 082dfc218f..5fc4cdd675 100644 --- a/assets/common/abilities/unique/quadmedbasic/singlestrike.ron +++ b/assets/common/abilities/unique/quadmedbasic/singlestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 120, damage_increase: 0, + base_poise_damage: 10, knockback: 5.0, range: 3.5, angle: 60.0, diff --git a/assets/common/abilities/unique/quadmedbasic/triplestrike.ron b/assets/common/abilities/unique/quadmedbasic/triplestrike.ron index 6588856b0d..cbec45247c 100644 --- a/assets/common/abilities/unique/quadmedbasic/triplestrike.ron +++ b/assets/common/abilities/unique/quadmedbasic/triplestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 120, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 120, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -28,6 +30,7 @@ ComboMelee( stage: 3, base_damage: 120, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, diff --git a/assets/common/abilities/unique/quadmedcharge/dash.ron b/assets/common/abilities/unique/quadmedcharge/dash.ron index 2e258451b7..fff283cfa5 100644 --- a/assets/common/abilities/unique/quadmedcharge/dash.ron +++ b/assets/common/abilities/unique/quadmedcharge/dash.ron @@ -2,6 +2,7 @@ DashMelee( energy_cost: 0, base_damage: 150, scaled_damage: 40, + base_poise_damage: 40, base_knockback: 8.0, scaled_knockback: 17.0, range: 4.0, diff --git a/assets/common/abilities/unique/quadmedcharge/doublestrike.ron b/assets/common/abilities/unique/quadmedcharge/doublestrike.ron index 51a8bf0985..d627b2ef72 100644 --- a/assets/common/abilities/unique/quadmedcharge/doublestrike.ron +++ b/assets/common/abilities/unique/quadmedcharge/doublestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 100, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 80, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 3.5, angle: 30.0, diff --git a/assets/common/abilities/unique/quadmedhoof/basic.ron b/assets/common/abilities/unique/quadmedhoof/basic.ron index f4fa2f61b5..77916236c2 100644 --- a/assets/common/abilities/unique/quadmedhoof/basic.ron +++ b/assets/common/abilities/unique/quadmedhoof/basic.ron @@ -4,7 +4,8 @@ BasicMelee( swing_duration: 500, recover_duration: 350, base_damage: 130, + base_poise_damage: 70, knockback: 25.0, range: 3.0, max_angle: 120.0, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/quadmedjump/doublestrike.ron b/assets/common/abilities/unique/quadmedjump/doublestrike.ron index c2c3fa1f4a..105824fcf5 100644 --- a/assets/common/abilities/unique/quadmedjump/doublestrike.ron +++ b/assets/common/abilities/unique/quadmedjump/doublestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 100, damage_increase: 0, + base_poise_damage: 10, knockback: 8.0, range: 3.5, angle: 30.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 80, damage_increase: 0, + base_poise_damage: 10, knockback: 8.0, range: 3.5, angle: 30.0, diff --git a/assets/common/abilities/unique/quadmedjump/leap.ron b/assets/common/abilities/unique/quadmedjump/leap.ron index 468dda4ebb..0752c561ad 100644 --- a/assets/common/abilities/unique/quadmedjump/leap.ron +++ b/assets/common/abilities/unique/quadmedjump/leap.ron @@ -5,6 +5,7 @@ LeapMelee( swing_duration: 75, recover_duration: 200, base_damage: 240, + base_poise_damage: 15, knockback: 12.0, range: 4.5, max_angle: 180.0, diff --git a/assets/common/abilities/unique/quadmedjump/quickleap.ron b/assets/common/abilities/unique/quadmedjump/quickleap.ron index c18f6e729a..4e119ee0ea 100644 --- a/assets/common/abilities/unique/quadmedjump/quickleap.ron +++ b/assets/common/abilities/unique/quadmedjump/quickleap.ron @@ -5,6 +5,7 @@ LeapMelee( swing_duration: 75, recover_duration: 125, base_damage: 120, + base_poise_damage: 15, knockback: 7.0, range: 4.5, max_angle: 180.0, diff --git a/assets/common/abilities/unique/quadmedquick/dash.ron b/assets/common/abilities/unique/quadmedquick/dash.ron index 325088f2c7..99e556e8ee 100644 --- a/assets/common/abilities/unique/quadmedquick/dash.ron +++ b/assets/common/abilities/unique/quadmedquick/dash.ron @@ -2,6 +2,7 @@ DashMelee( energy_cost: 0, base_damage: 130, scaled_damage: 20, + base_poise_damage: 30, base_knockback: 8.0, scaled_knockback: 7.0, range: 2.0, diff --git a/assets/common/abilities/unique/quadmedquick/triplestrike.ron b/assets/common/abilities/unique/quadmedquick/triplestrike.ron index 06057f3178..f853e22543 100644 --- a/assets/common/abilities/unique/quadmedquick/triplestrike.ron +++ b/assets/common/abilities/unique/quadmedquick/triplestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 150, damage_increase: 0, + base_poise_damage: 10, knockback: 5.0, range: 3.5, angle: 60.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 150, damage_increase: 0, + base_poise_damage: 10, knockback: 5.0, range: 3.5, angle: 60.0, @@ -28,6 +30,7 @@ ComboMelee( stage: 3, base_damage: 150, damage_increase: 0, + base_poise_damage: 10, knockback: 5.0, range: 3.5, angle: 60.0, diff --git a/assets/common/abilities/unique/quadsmallbasic/singlestrike.ron b/assets/common/abilities/unique/quadsmallbasic/singlestrike.ron index 3870ba9037..d2f2f8262b 100644 --- a/assets/common/abilities/unique/quadsmallbasic/singlestrike.ron +++ b/assets/common/abilities/unique/quadsmallbasic/singlestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 30, damage_increase: 0, + base_poise_damage: 10, knockback: 5.0, range: 3.5, angle: 60.0, @@ -20,4 +21,4 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/stonegolemfist/basic.ron b/assets/common/abilities/unique/stonegolemfist/basic.ron index 2ce4d66744..b42bbcd256 100644 --- a/assets/common/abilities/unique/stonegolemfist/basic.ron +++ b/assets/common/abilities/unique/stonegolemfist/basic.ron @@ -5,6 +5,7 @@ BasicMelee( recover_duration: 250, knockback: 25.0, base_damage: 200, + base_poise_damage: 80, range: 5.0, max_angle: 120.0, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/stonegolemfist/shockwave.ron b/assets/common/abilities/unique/stonegolemfist/shockwave.ron index d161a02d62..47ec08b62a 100644 --- a/assets/common/abilities/unique/stonegolemfist/shockwave.ron +++ b/assets/common/abilities/unique/stonegolemfist/shockwave.ron @@ -4,6 +4,7 @@ Shockwave( swing_duration: 200, recover_duration: 800, damage: 500, + poise_damage: 50, knockback: TowardsUp(40.0), shockwave_angle: 90.0, shockwave_vertical_angle: 90.0, @@ -11,4 +12,4 @@ Shockwave( shockwave_duration: 1000, requires_ground: true, move_efficiency: 0.05, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/theropodbasic/singlestrike.ron b/assets/common/abilities/unique/theropodbasic/singlestrike.ron index 2e6da823b3..4b4878dfe5 100644 --- a/assets/common/abilities/unique/theropodbasic/singlestrike.ron +++ b/assets/common/abilities/unique/theropodbasic/singlestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 150, damage_increase: 0, + base_poise_damage: 15, knockback: 5.0, range: 7.5, angle: 60.0, @@ -20,4 +21,4 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/theropodbasic/triplestrike.ron b/assets/common/abilities/unique/theropodbasic/triplestrike.ron index 92127a6454..739aaf9234 100644 --- a/assets/common/abilities/unique/theropodbasic/triplestrike.ron +++ b/assets/common/abilities/unique/theropodbasic/triplestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 170, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 7.5, angle: 30.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 190, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 5.5, angle: 30.0, @@ -28,6 +30,7 @@ ComboMelee( stage: 3, base_damage: 230, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 5.5, angle: 30.0, diff --git a/assets/common/abilities/unique/theropodbird/singlestrike.ron b/assets/common/abilities/unique/theropodbird/singlestrike.ron index 0b9a84f80e..c19995d2b6 100644 --- a/assets/common/abilities/unique/theropodbird/singlestrike.ron +++ b/assets/common/abilities/unique/theropodbird/singlestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 150, damage_increase: 0, + base_poise_damage: 10, knockback: 5.0, range: 5.5, angle: 5.0, @@ -20,4 +21,4 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, -) \ No newline at end of file +) diff --git a/assets/common/abilities/unique/theropodbird/triplestrike.ron b/assets/common/abilities/unique/theropodbird/triplestrike.ron index cf59834a8b..4c33e0bbe9 100644 --- a/assets/common/abilities/unique/theropodbird/triplestrike.ron +++ b/assets/common/abilities/unique/theropodbird/triplestrike.ron @@ -4,6 +4,7 @@ ComboMelee( stage: 1, base_damage: 170, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 4.5, angle: 5.0, @@ -16,6 +17,7 @@ ComboMelee( stage: 2, base_damage: 190, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 4.0, angle: 10.0, @@ -28,6 +30,7 @@ ComboMelee( stage: 3, base_damage: 230, damage_increase: 0, + base_poise_damage: 10, knockback: 10.0, range: 4.0, angle: 10.0, diff --git a/common/net/src/msg/ecs_packet.rs b/common/net/src/msg/ecs_packet.rs index 25860655e7..599115dcd1 100644 --- a/common/net/src/msg/ecs_packet.rs +++ b/common/net/src/msg/ecs_packet.rs @@ -18,6 +18,7 @@ sum_type! { Auras(comp::Auras), Energy(comp::Energy), Health(comp::Health), + Poise(comp::Poise), LightEmitter(comp::LightEmitter), Inventory(comp::Inventory), Item(comp::Item), @@ -50,6 +51,7 @@ sum_type! { Auras(PhantomData), Energy(PhantomData), Health(PhantomData), + Poise(PhantomData), LightEmitter(PhantomData), Inventory(PhantomData), Item(PhantomData), @@ -82,6 +84,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Auras(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Energy(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Health(comp) => sync::handle_insert(comp, entity, world), + EcsCompPacket::Poise(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::LightEmitter(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Inventory(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Item(comp) => sync::handle_insert(comp, entity, world), @@ -112,6 +115,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Auras(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Energy(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Health(comp) => sync::handle_modify(comp, entity, world), + EcsCompPacket::Poise(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::LightEmitter(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Inventory(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Item(comp) => sync::handle_modify(comp, entity, world), @@ -142,6 +146,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPhantom::Auras(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Energy(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Health(_) => sync::handle_remove::(entity, world), + EcsCompPhantom::Poise(_) => sync::handle_remove::(entity, world), EcsCompPhantom::LightEmitter(_) => { sync::handle_remove::(entity, world) }, diff --git a/common/src/combat.rs b/common/src/combat.rs index 1ba9de057d..00d9b00c7f 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -40,6 +40,7 @@ pub enum DamageSource { pub struct Damage { pub source: DamageSource, pub value: f32, + pub poise_damage: f32, } impl Damage { @@ -68,7 +69,6 @@ impl Damage { pub fn modify_damage(self, inventory: Option<&Inventory>, uid: Option) -> HealthChange { let mut damage = self.value; let damage_reduction = inventory.map_or(0.0, |inv| Damage::compute_damage_reduction(inv)); - match self.source { DamageSource::Melee => { // Critical hit @@ -84,13 +84,16 @@ impl Damage { damage += critdamage; } - HealthChange { - amount: -damage as i32, - cause: HealthSource::Damage { - kind: self.source, - by: uid, + ( + HealthChange { + amount: -damage as i32, + cause: HealthSource::Damage { + kind: self.source, + by: uid, + }, }, - } + -poise_damage as i32, + ) }, DamageSource::Projectile => { // Critical hit @@ -100,63 +103,82 @@ impl Damage { // Armor damage *= 1.0 - damage_reduction; - HealthChange { - amount: -damage as i32, - cause: HealthSource::Damage { - kind: self.source, - by: uid, + ( + HealthChange { + amount: -damage as i32, + cause: HealthSource::Damage { + kind: self.source, + by: uid, + }, }, - } + -poise_damage as i32, + ) }, DamageSource::Explosion => { // Armor damage *= 1.0 - damage_reduction; - HealthChange { - amount: -damage as i32, - cause: HealthSource::Damage { - kind: self.source, - by: uid, + ( + HealthChange { + amount: -damage as i32, + cause: HealthSource::Damage { + kind: self.source, + by: uid, + }, }, - } + -poise_damage as i32, + ) }, DamageSource::Shockwave => { // Armor damage *= 1.0 - damage_reduction; - HealthChange { - amount: -damage as i32, - cause: HealthSource::Damage { - kind: self.source, - by: uid, + ( + HealthChange { + amount: -damage as i32, + cause: HealthSource::Damage { + kind: self.source, + by: uid, + }, }, - } + -poise_damage as i32, + ) }, DamageSource::Energy => { // Armor damage *= 1.0 - damage_reduction; - HealthChange { - amount: -damage as i32, - cause: HealthSource::Damage { - kind: self.source, - by: uid, + ( + HealthChange { + amount: -damage as i32, + cause: HealthSource::Damage { + kind: self.source, + by: uid, + }, }, - } - }, - DamageSource::Healing => HealthChange { - amount: damage as i32, - cause: HealthSource::Heal { by: uid }, + -poise_damage as i32, + ) }, + DamageSource::Healing => ( + HealthChange { + amount: damage as i32, + cause: HealthSource::Heal { by: uid }, + }, + 0, + ), DamageSource::Falling => { // Armor if (damage_reduction - 1.0).abs() < f32::EPSILON { damage = 0.0; + poise_damage = 0.0; } - HealthChange { - amount: -damage as i32, - cause: HealthSource::World, - } + ( + HealthChange { + amount: -damage as i32, + cause: HealthSource::World, + }, + -poise_damage as i32, + ) }, DamageSource::Buff(_) => HealthChange { amount: -damage as i32, @@ -171,7 +193,8 @@ impl Damage { kind: self.source, by: uid, }, - }, + -poise_damage as i32, + ), } } diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 6b487708f7..ec0bfe2f02 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -61,6 +61,7 @@ pub enum CharacterAbility { swing_duration: u64, recover_duration: u64, base_damage: u32, + base_poise_damage: u32, knockback: f32, range: f32, max_angle: f32, @@ -98,6 +99,7 @@ pub enum CharacterAbility { energy_cost: u32, base_damage: u32, scaled_damage: u32, + base_poise_damage: u32, base_knockback: f32, scaled_knockback: f32, range: f32, @@ -137,6 +139,7 @@ pub enum CharacterAbility { swing_duration: u64, recover_duration: u64, base_damage: u32, + base_poise_damage: u32, range: f32, max_angle: f32, knockback: f32, @@ -148,6 +151,7 @@ pub enum CharacterAbility { swing_duration: u64, recover_duration: u64, base_damage: u32, + base_poise_damage: u32, knockback: f32, range: f32, energy_cost: u32, @@ -162,6 +166,7 @@ pub enum CharacterAbility { energy_drain: u32, initial_damage: u32, scaled_damage: u32, + initial_poise_damage: u32, initial_knockback: f32, scaled_knockback: f32, range: f32, @@ -177,6 +182,7 @@ pub enum CharacterAbility { energy_drain: u32, initial_damage: u32, scaled_damage: u32, + initial_poise_damage: u32, initial_knockback: f32, scaled_knockback: f32, speed: f32, @@ -196,6 +202,7 @@ pub enum CharacterAbility { swing_duration: u64, recover_duration: u64, damage: u32, + poise_damage: u32, knockback: Knockback, shockwave_angle: f32, shockwave_vertical_angle: f32, @@ -228,6 +235,7 @@ impl Default for CharacterAbility { swing_duration: 250, recover_duration: 500, base_damage: 10, + base_poise_damage: 10, knockback: 0.0, range: 3.5, max_angle: 15.0, @@ -1071,6 +1079,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration, recover_duration, base_damage, + base_poise_damage, knockback, range, max_angle, @@ -1081,6 +1090,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration: Duration::from_millis(*swing_duration), recover_duration: Duration::from_millis(*recover_duration), base_damage: *base_damage, + base_poise_damage: *base_poise_damage, knockback: *knockback, range: *range, max_angle: *max_angle, @@ -1131,6 +1141,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { energy_cost: _, base_damage, scaled_damage, + base_poise_damage, base_knockback, scaled_knockback, range, @@ -1147,6 +1158,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { static_data: dash_melee::StaticData { base_damage: *base_damage, scaled_damage: *scaled_damage, + base_poise_damage: *base_poise_damage, base_knockback: *base_knockback, scaled_knockback: *scaled_knockback, range: *range, @@ -1224,6 +1236,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration, recover_duration, base_damage, + base_poise_damage, knockback, range, max_angle, @@ -1236,6 +1249,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration: Duration::from_millis(*swing_duration), recover_duration: Duration::from_millis(*recover_duration), base_damage: *base_damage, + base_poise_damage: *base_poise_damage, knockback: *knockback, range: *range, max_angle: *max_angle, @@ -1252,6 +1266,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration, recover_duration, base_damage, + base_poise_damage, knockback, range, energy_cost, @@ -1266,6 +1281,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration: Duration::from_millis(*swing_duration), recover_duration: Duration::from_millis(*recover_duration), base_damage: *base_damage, + base_poise_damage: *base_damage, knockback: *knockback, range: *range, energy_cost: *energy_cost, @@ -1286,6 +1302,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { energy_drain, initial_damage, scaled_damage, + initial_poise_damage, initial_knockback, scaled_knockback, speed, @@ -1301,6 +1318,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { energy_drain: *energy_drain, initial_damage: *initial_damage, scaled_damage: *scaled_damage, + initial_poise_damage: *initial_poise_damage, initial_knockback: *initial_knockback, scaled_knockback: *scaled_knockback, speed: *speed, @@ -1322,6 +1340,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { energy_drain, initial_damage, scaled_damage, + initial_poise_damage, initial_knockback, scaled_knockback, speed, @@ -1342,6 +1361,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { energy_drain: *energy_drain, initial_damage: *initial_damage, scaled_damage: *scaled_damage, + initial_poise_damage: *initial_poise_damage, speed: *speed, initial_knockback: *initial_knockback, scaled_knockback: *scaled_knockback, @@ -1394,6 +1414,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration, recover_duration, damage, + poise_damage, knockback, shockwave_angle, shockwave_vertical_angle, @@ -1407,6 +1428,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration: Duration::from_millis(*swing_duration), recover_duration: Duration::from_millis(*recover_duration), damage: *damage, + poise_damage: *poise_damage, knockback: *knockback, shockwave_angle: *shockwave_angle, shockwave_vertical_angle: *shockwave_vertical_angle, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index bd6ddc6775..67bd47876e 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -444,6 +444,14 @@ impl Body { // TODO: Match on species pub fn combat_multiplier(&self) -> f32 { if let Body::Object(_) = self { 0.0 } else { 1.0 } } + pub fn base_poise(&self) -> u32 { + match self { + Body::Humanoid(_) => 100, + Body::BipedLarge(_) => 200, + _ => 100, + } + } + #[allow(unreachable_patterns)] pub fn base_exp(&self) -> u32 { match self { @@ -602,6 +610,13 @@ impl Body { } } + pub fn base_poise_dmg(&self) -> u32 { + match self { + Body::Humanoid(_) => 100, + _ => 50, + } + } + pub fn base_range(&self) -> f32 { match self { Body::Humanoid(_) => 5.0, diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 92c2633ea4..b52ff21b16 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -44,6 +44,10 @@ pub enum CharacterState { Sneak, Glide, GlideWield, + /// A stunned state + Stunned(stunned::Data), + /// A stagger state similar to a stun but knocked to the ground + Staggered(staggered::Data), /// A basic blocking state BasicBlock, /// Player is busy equipping or unequipping weapons @@ -104,6 +108,10 @@ impl CharacterState { matches!(self, CharacterState::Sneak | CharacterState::Roll(_)) } + pub fn is_stunned(&self) -> bool { + matches!(self, CharacterState::Stunned { .. } | CharacterState::Staggered { .. }) + } + pub fn is_attack(&self) -> bool { matches!( self, diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 2b3d47aeaf..ef15d30e1d 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -19,6 +19,7 @@ mod location; mod misc; mod phys; mod player; +pub mod poise; pub mod projectile; pub mod shockwave; pub mod skills; @@ -65,6 +66,7 @@ pub use phys::{ Sticky, Vel, }; pub use player::Player; +pub use poise::{Poise, PoiseState}; pub use projectile::{Projectile, ProjectileConstructor}; pub use shockwave::{Shockwave, ShockwaveHitEntities}; pub use skills::{Skill, SkillGroup, SkillGroupKind, SkillSet}; diff --git a/common/src/comp/poise.rs b/common/src/comp/poise.rs new file mode 100644 index 0000000000..a626b0134a --- /dev/null +++ b/common/src/comp/poise.rs @@ -0,0 +1,106 @@ +use crate::{comp::Body, sync::Uid, DamageSource}; +use serde::{Deserialize, Serialize}; +use specs::{Component, FlaggedStorage}; +use specs_idvs::IdvStorage; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct Poise { + base_max: u32, + current: u32, + maximum: u32, + pub is_interrupted: bool, + pub is_stunned: bool, + pub is_dazed: bool, + pub is_knockeddown: bool, +} + +impl Default for Poise { + fn default() -> Self { + Self { + current: 0, + maximum: 0, + base_max: 0, + is_interrupted: false, + is_stunned: false, + is_dazed: false, + is_knockeddown: false, + } + } +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum PoiseState { + Normal, + Interrupted, + Stunned, + Dazed, + KnockedDown, +} + +impl Poise { + pub fn new(body: Body) -> Self { + let mut poise = Poise::default(); + poise.update_max_poise(Some(body)); + poise.set_to(poise.maximum()); + + poise + } + + pub fn poise_state(&self) -> PoiseState { + if self.current >= 5 * self.maximum / 10 { + PoiseState::Normal + } else if self.current >= 4 * self.maximum / 10 { + PoiseState::Interrupted + } else if self.current >= 3 * self.maximum / 10 { + PoiseState::Stunned + } else if self.current >= 2 * self.maximum / 10 { + PoiseState::Dazed + } else { + PoiseState::KnockedDown + } + } + + pub fn current(&self) -> u32 { self.current } + + pub fn maximum(&self) -> u32 { self.maximum } + + pub fn set_to(&mut self, amount: u32) { + let amount = amount.min(self.maximum); + self.current = amount; + } + + pub fn change_by(&mut self, change: i32) { + self.current = ((self.current as i32 + change).max(0) as u32).min(self.maximum); + } + + pub fn reset(&mut self) { self.current = self.maximum; } + + pub fn set_maximum(&mut self, amount: u32) { + self.maximum = amount; + self.current = self.current.min(self.maximum); + } + + fn set_base_max(&mut self, amount: u32) { + self.base_max = amount; + self.current = self.current.min(self.maximum); + } + + pub fn reset_max(&mut self) { self.maximum = self.base_max; } + + pub fn update_max_poise(&mut self, body: Option) { + if let Some(body) = body { + self.set_base_max(body.base_poise()); + self.set_maximum(body.base_poise()); + } + } + + pub fn with_max_poise(mut self, amount: u32) -> Self { + self.maximum = amount; + self.current = amount; + self + } +} + +impl Component for Poise { + type Storage = FlaggedStorage>; +} diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 60c9a2b463..efd50b4608 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -88,6 +88,7 @@ impl ProjectileConstructor { Effect::Damage(Some(GroupTarget::OutOfGroup), Damage { source: DamageSource::Projectile, value: damage, + poise_damage: 0.0, }), Effect::Knockback(Knockback::Away(knockback)), Effect::RewardEnergy(energy_regen), @@ -115,6 +116,7 @@ impl ProjectileConstructor { effect::Effect::Damage(Damage { source: DamageSource::Explosion, value: damage, + poise_damage: 10.0, }), ), RadiusEffect::TerrainDestruction(2.0), @@ -131,6 +133,7 @@ impl ProjectileConstructor { effect::Effect::Damage(Damage { source: DamageSource::Explosion, value: damage, + poise_damage: 10.0, }), )], radius, @@ -172,6 +175,7 @@ impl ProjectileConstructor { effect::Effect::Damage(Damage { source: DamageSource::Explosion, value: damage, + poise_damage: 0.0, }), ), RadiusEffect::Entity( @@ -179,6 +183,7 @@ impl ProjectileConstructor { effect::Effect::Damage(Damage { source: DamageSource::Healing, value: heal, + poise_damage: 0.0, }), ), ], @@ -195,6 +200,7 @@ impl ProjectileConstructor { effect::Effect::Damage(Damage { source: DamageSource::Explosion, value: damage, + poise_damage: 0.0, }), ), RadiusEffect::Entity( @@ -202,6 +208,7 @@ impl ProjectileConstructor { effect::Effect::Damage(Damage { source: DamageSource::Healing, value: heal, + poise_damage: 0.0, }), ), ], diff --git a/common/src/effect.rs b/common/src/effect.rs index 9e123de118..39679d5817 100644 --- a/common/src/effect.rs +++ b/common/src/effect.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Effect { Health(comp::HealthChange), + Poise(i32), Damage(combat::Damage), Buff(BuffEffect), } @@ -21,6 +22,7 @@ impl Effect { pub fn info(&self) -> String { match self { Effect::Health(c) => format!("{:+} health", c.amount), + Effect::Poise(c) => format!("{:+} poise", c), Effect::Damage(d) => format!("{:+}", d.value), Effect::Buff(e) => format!("{:?} buff", e), } @@ -31,6 +33,9 @@ impl Effect { Effect::Health(change) => { change.amount = (change.amount as f32 * modifier) as i32; }, + Effect::Poise(change) => { + *change = (*change as f32 * modifier) as i32; + }, Effect::Damage(damage) => { damage.interpolate_damage(modifier, 0.0); }, diff --git a/common/src/event.rs b/common/src/event.rs index cb9aa0d858..56345c95b2 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -34,7 +34,7 @@ pub enum ServerEvent { }, Damage { entity: EcsEntity, - change: comp::HealthChange, + change: (comp::HealthChange, i32), }, Delete(EcsEntity), Destroy { @@ -98,6 +98,7 @@ pub enum ServerEvent { pos: comp::Pos, stats: comp::Stats, health: comp::Health, + poise: comp::Poise, loadout: comp::inventory::loadout::Loadout, body: comp::Body, agent: Option, diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 07bac71c76..5419f4e6fa 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -122,10 +122,12 @@ impl CharacterBehavior for Data { let damage = Damage { source: DamageSource::Energy, value: self.static_data.base_dps as f32 / self.static_data.tick_rate, + poise_damage: 0.0, }; let heal = Damage { source: DamageSource::Healing, value: self.static_data.base_hps as f32 / self.static_data.tick_rate, + poise_damage: 0.0, }; let speed = self.static_data.range / self.static_data.beam_duration.as_secs_f32(); diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index dc3a1e94cd..7000002b56 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -20,6 +20,8 @@ pub struct StaticData { pub recover_duration: Duration, /// Base damage pub base_damage: u32, + /// Base poise reduction + pub base_poise_damage: u32, /// Knockback pub knockback: f32, /// Max range @@ -92,6 +94,7 @@ impl CharacterBehavior for Data { damages: vec![(Some(GroupTarget::OutOfGroup), Damage { source: DamageSource::Melee, value: self.static_data.base_damage as f32, + poise_damage: self.static_data.base_poise_damage as f32, })], range: self.static_data.range, max_angle: 180_f32.to_radians(), diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index f2b6f1ed7c..0ee227e3fe 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ Attacking, Beam, Body, CharacterState, ControlAction, Controller, ControllerInputs, Energy, - Health, Inventory, Ori, PhysicsState, Pos, StateUpdate, Stats, Vel, + Health, Inventory, Ori, PhysicsState, Poise, Pos, StateUpdate, Stats, Vel, }, resources::DeltaTime, uid::Uid, @@ -51,6 +51,7 @@ pub struct JoinData<'a> { pub controller: &'a Controller, pub inputs: &'a ControllerInputs, pub health: &'a Health, + pub poise: Option<&'a Poise>, pub energy: &'a Energy, pub inventory: &'a Inventory, pub body: &'a Body, @@ -80,6 +81,7 @@ pub type JoinTuple<'a> = ( RestrictedMut<'a, Inventory>, &'a mut Controller, &'a Health, + Option<&'a Poise>, &'a Body, &'a PhysicsState, Option<&'a Attacking>, @@ -101,10 +103,11 @@ impl<'a> JoinData<'a> { controller: j.8, inputs: &j.8.inputs, health: j.9, - body: j.10, - physics: j.11, - attacking: j.12, - stats: j.14, + poise: j.10, + body: j.11, + physics: j.12, + attacking: j.13, + stats: j.15, updater, dt, } diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 21821d3a0c..bc9aa1c49e 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -24,6 +24,8 @@ pub struct StaticData { pub initial_knockback: f32, /// How much the knockback is scaled by pub scaled_knockback: f32, + /// Initial poise damage + pub initial_poise_damage: u32, /// Max range pub range: f32, /// Max angle (45.0 will give you a 90.0 angle window) @@ -152,6 +154,7 @@ impl CharacterBehavior for Data { source: DamageSource::Melee, value: self.static_data.initial_damage as f32 + self.charge_amount * self.static_data.scaled_damage as f32, + poise_damage: self.static_data.initial_poise_damage as f32, }; let knockback = self.static_data.initial_knockback + self.charge_amount * self.static_data.scaled_knockback; diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 90c0ec468b..521224246b 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -30,6 +30,8 @@ pub struct StaticData { pub initial_damage: u32, /// How much the damage scales as it is charged pub scaled_damage: u32, + /// Initial poise damage + pub initial_poise_damage: u32, /// How much knockback there is with no charge pub initial_knockback: f32, /// How much the knockback scales as it is charged @@ -106,6 +108,7 @@ impl CharacterBehavior for Data { source: DamageSource::Projectile, value: self.static_data.initial_damage as f32 + charge_frac * self.static_data.scaled_damage as f32, + poise_damage: self.static_data.initial_poise_damage as f32, }; let knockback = self.static_data.initial_knockback + charge_frac * self.static_data.scaled_knockback; diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index e432e693ad..e344751fb0 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -17,6 +17,8 @@ pub struct Stage { pub base_damage: u32, /// Damage scaling per combo pub damage_increase: u32, + /// Initial poise damage of stage + pub base_poise_damage: u32, /// Knockback of stage pub knockback: f32, /// Range of attack @@ -40,6 +42,7 @@ impl Stage { stage: self.stage, base_damage: self.base_damage, damage_increase: self.damage_increase, + base_poise_damage: self.base_poise_damage, knockback: self.knockback, range: self.range, angle: self.angle, @@ -163,10 +166,12 @@ impl CharacterBehavior for Data { .scales_from_combo .min(self.combo / self.static_data.num_stages) * self.static_data.stage_data[stage_index].damage_increase; + let poise_damage = self.static_data.stage_data[stage_index].base_poise_damage; data.updater.insert(data.entity, Attacking { damages: vec![(Some(GroupTarget::OutOfGroup), Damage { source: DamageSource::Melee, value: damage as f32, + poise_damage: poise_damage as f32, })], range: self.static_data.stage_data[stage_index].range, max_angle: self.static_data.stage_data[stage_index].angle.to_radians(), diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 597bd33f7b..1f4365ff0a 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -17,6 +17,8 @@ pub struct StaticData { pub base_damage: u32, /// How much the attack scales in damage pub scaled_damage: u32, + /// Initial poise damage + pub base_poise_damage: u32, /// How much the attack knocks the target back initially pub base_knockback: f32, /// How much the attack scales in knockback @@ -129,6 +131,7 @@ impl CharacterBehavior for Data { source: DamageSource::Melee, value: self.static_data.base_damage as f32 + charge_frac * self.static_data.scaled_damage as f32, + poise_damage: self.static_data.base_poise_damage as f32, }; let knockback = self.static_data.base_knockback + charge_frac * self.static_data.scaled_knockback; diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 268cbfefd5..c21f41e7c5 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -22,6 +22,8 @@ pub struct StaticData { pub recover_duration: Duration, /// Base damage pub base_damage: u32, + /// Base poise damage + pub base_poise_damage: u32, /// Knockback pub knockback: f32, /// Max range @@ -150,6 +152,7 @@ impl CharacterBehavior for Data { damages: vec![(Some(GroupTarget::OutOfGroup), Damage { source: DamageSource::Melee, value: self.static_data.base_damage as f32, + poise_damage: self.static_data.base_poise_damage as f32, })], range: self.static_data.range, max_angle: self.static_data.max_angle.to_radians(), diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index d5a1c5e4fe..e451032719 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -21,5 +21,7 @@ pub mod shockwave; pub mod sit; pub mod sneak; pub mod spin_melee; +pub mod staggered; +pub mod stunned; pub mod utils; pub mod wielding; diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index 8d0b7270e6..8f9a49564a 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -21,6 +21,8 @@ pub struct StaticData { pub recover_duration: Duration, /// Base damage pub damage: u32, + /// Base poise damage + pub poise_damage: u32, /// Knockback pub knockback: Knockback, /// Angle of the shockwave @@ -86,6 +88,7 @@ impl CharacterBehavior for Data { damages: vec![(Some(GroupTarget::OutOfGroup), Damage { source: DamageSource::Shockwave, value: self.static_data.damage as f32, + poise_damage: self.static_data.poise_damage as f32, })], knockback: self.static_data.knockback, requires_ground: self.static_data.requires_ground, diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 9d301f3e6c..43b330f95c 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -22,6 +22,8 @@ pub struct StaticData { pub recover_duration: Duration, /// Base damage pub base_damage: u32, + /// Base poise damage + pub base_poise_damage: u32, /// Knockback pub knockback: f32, /// Range @@ -114,6 +116,7 @@ impl CharacterBehavior for Data { damages: vec![(Some(GroupTarget::OutOfGroup), Damage { source: DamageSource::Melee, value: self.static_data.base_damage as f32, + poise_damage: self.static_data.base_damage as f32, })], range: self.static_data.range, max_angle: 180_f32.to_radians(), diff --git a/common/src/states/staggered.rs b/common/src/states/staggered.rs new file mode 100644 index 0000000000..44b2127b12 --- /dev/null +++ b/common/src/states/staggered.rs @@ -0,0 +1,89 @@ +use super::utils::*; +use crate::{ + comp::{CharacterState, StateUpdate}, + states::behavior::{CharacterBehavior, JoinData}, + Knockback, +}; +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 { + /// How long until state begins to exit + pub buildup_duration: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// Knockback + pub knockback: Knockback, +} + +#[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 character was wielding or not + pub was_wielded: bool, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + println!("staggered"); + let mut update = StateUpdate::from(data); + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::Staggered(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + // Transitions to recovery section of stage + update.character = CharacterState::Staggered(Data { + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + // Recovery + update.character = CharacterState::Staggered(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } 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/stunned.rs b/common/src/states/stunned.rs new file mode 100644 index 0000000000..16d609efbf --- /dev/null +++ b/common/src/states/stunned.rs @@ -0,0 +1,89 @@ +use super::utils::*; +use crate::{ + comp::{CharacterState, StateUpdate}, + states::behavior::{CharacterBehavior, JoinData}, + Knockback, +}; +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 { + /// How long until state begins to exit + pub buildup_duration: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// Knockback + pub knockback: Knockback, +} + +#[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 character was wielding or not + pub was_wielded: bool, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + println!("stunned"); + let mut update = StateUpdate::from(data); + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::Stunned(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + // Transitions to recovery section of stage + update.character = CharacterState::Stunned(Data { + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + // Recovery + update.character = CharacterState::Stunned(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } 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/sys/src/beam.rs b/common/sys/src/beam.rs index 3ab6cc6612..46b07c58c6 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -174,14 +174,17 @@ impl<'a> System<'a> for Sys { if let Some(entity) = beam_owner { server_emitter.emit(ServerEvent::Damage { entity, - change: HealthChange { - amount: (-change.amount as f32 - * beam_segment.lifesteal_eff) - as i32, - cause: HealthSource::Heal { - by: beam_segment.owner, + change: ( + HealthChange { + amount: (-change.0.amount as f32 + * beam_segment.lifesteal_eff) + as i32, + cause: HealthSource::Heal { + by: beam_segment.owner, + }, }, - }, + 0, + ), }); server_emitter.emit(ServerEvent::EnergyChange { entity, diff --git a/common/sys/src/buff.rs b/common/sys/src/buff.rs index b7a12fbfef..f6d0d5b1c2 100644 --- a/common/sys/src/buff.rs +++ b/common/sys/src/buff.rs @@ -114,7 +114,7 @@ impl<'a> System<'a> for Sys { }; server_emitter.emit(ServerEvent::Damage { entity, - change: HealthChange { amount, cause }, + change: (HealthChange { amount, cause }, 0), }); *accumulated = 0.0; }; diff --git a/common/sys/src/character_behavior.rs b/common/sys/src/character_behavior.rs index 37a055060a..c86b0a45e9 100644 --- a/common/sys/src/character_behavior.rs +++ b/common/sys/src/character_behavior.rs @@ -4,7 +4,7 @@ use common::{ comp::{ inventory::slot::{EquipSlot, Slot}, Attacking, Beam, Body, CharacterState, Controller, Energy, Health, Inventory, Mounting, - Ori, PhysicsState, Pos, StateUpdate, Stats, Vel, + Ori, PhysicsState, Poise, Pos, StateUpdate, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, @@ -65,6 +65,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Inventory>, WriteStorage<'a, Controller>, ReadStorage<'a, Health>, + ReadStorage<'a, Poise>, ReadStorage<'a, Body>, ReadStorage<'a, PhysicsState>, ReadStorage<'a, Attacking>, @@ -93,6 +94,7 @@ impl<'a> System<'a> for Sys { mut inventories, mut controllers, healths, + poises, bodies, physics_states, attacking_storage, @@ -118,6 +120,7 @@ impl<'a> System<'a> for Sys { &mut inventories.restrict_mut(), &mut controllers, &healths, + poises.maybe(), &bodies, &physics_states, attacking_storage.maybe(), @@ -151,6 +154,8 @@ impl<'a> System<'a> for Sys { CharacterState::GlideWield => { states::glide_wield::Data.handle_event(&j, action) }, + CharacterState::Stunned(data) => data.handle_event(&j, action), + CharacterState::Staggered(data) => data.handle_event(&j, action), CharacterState::Sit => { states::sit::Data::handle_event(&states::sit::Data, &j, action) }, @@ -191,6 +196,8 @@ impl<'a> System<'a> for Sys { CharacterState::Climb => states::climb::Data.behavior(&j), CharacterState::Glide => states::glide::Data.behavior(&j), CharacterState::GlideWield => states::glide_wield::Data.behavior(&j), + CharacterState::Stunned(data) => data.behavior(&j), + CharacterState::Staggered(data) => data.behavior(&j), CharacterState::Sit => states::sit::Data::behavior(&states::sit::Data, &j), CharacterState::Dance => states::dance::Data::behavior(&states::dance::Data, &j), CharacterState::Sneak => states::sneak::Data::behavior(&states::sneak::Data, &j), diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index dffb3b4407..305b7f175b 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -132,14 +132,14 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::Damage { entity: b, change }); // Apply bleeding buff on melee hits with 10% chance // TODO: Don't have buff uniformly applied on all melee attacks - if change.amount < 0 && thread_rng().gen::() < 0.1 { + if change.0.amount < 0 && thread_rng().gen::() < 0.1 { use buff::*; server_emitter.emit(ServerEvent::Buff { entity: b, buff_change: BuffChange::Add(Buff::new( BuffKind::Bleeding, BuffData { - strength: -change.amount as f32 / 10.0, + strength: -change.0.amount as f32 / 10.0, duration: Some(Duration::from_secs(10)), }, vec![BuffCategory::Physical], diff --git a/common/sys/src/state.rs b/common/sys/src/state.rs index e16af077e9..c5ba883d4c 100644 --- a/common/sys/src/state.rs +++ b/common/sys/src/state.rs @@ -116,6 +116,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); diff --git a/common/sys/src/stats.rs b/common/sys/src/stats.rs index 26137611b5..7cbe221915 100644 --- a/common/sys/src/stats.rs +++ b/common/sys/src/stats.rs @@ -214,7 +214,10 @@ impl<'a> System<'a> for Sys { }, // Non-combat abilities that consume energy; // temporarily stall energy gain, but preserve regen_rate. - CharacterState::Roll { .. } | CharacterState::Climb { .. } => {}, + CharacterState::Roll { .. } + | CharacterState::Climb { .. } + | CharacterState::Stunned { .. } + | CharacterState::Staggered { .. } => {}, } } sys_metrics.stats_ns.store( diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 0f50f4fa9c..38126a846f 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -825,6 +825,7 @@ fn handle_spawn( npc::BodyType::from_body(body), )), comp::Health::new(body, 1), + comp::Poise::new(body), inventory, body, ) @@ -932,10 +933,11 @@ fn handle_spawn_training_dummy( let stats = comp::Stats::new("Training Dummy".to_string()); let health = comp::Health::new(body, 0); + let poise = comp::Poise::new(body); server .state - .create_npc(pos, stats, health, Inventory::new_empty(), body) + .create_npc(pos, stats, health, poise, Inventory::new_empty(), body) .with(comp::Vel(vel)) .with(comp::MountState::Unmounted) .build(); @@ -1363,6 +1365,7 @@ fn handle_explosion( Effect::Damage(Damage { source: DamageSource::Explosion, value: 100.0 * power, + poise_damage: 100.0, }), ), RadiusEffect::TerrainDestruction(power), diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 2e6154a69d..03fa9dd55a 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -9,7 +9,7 @@ use common::{ group, inventory::loadout::Loadout, shockwave, Agent, Alignment, Body, Gravity, Health, HomeChunk, Inventory, Item, ItemDrop, - LightEmitter, Ori, Pos, Projectile, Scale, Stats, Vel, WaypointArea, + LightEmitter, Ori, Poise, Pos, Projectile, Scale, Stats, Vel, WaypointArea, }, outcome::Outcome, rtsim::RtSimEntity, @@ -49,6 +49,7 @@ pub fn handle_create_npc( pos: Pos, stats: Stats, health: Health, + poise: Poise, loadout: Loadout, body: Body, agent: impl Into>, @@ -71,7 +72,7 @@ pub fn handle_create_npc( let entity = server .state - .create_npc(pos, stats, health, inventory, body) + .create_npc(pos, stats, health, poise, inventory, body) .with(scale) .with(alignment); diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index d8aad62ceb..2c475ac275 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -14,7 +14,7 @@ use common::{ self, aura, buff, chat::{KillSource, KillType}, object, Alignment, Body, Energy, EnergyChange, Group, Health, HealthChange, HealthSource, - Inventory, Item, Player, Pos, Stats, + Inventory, Item, Player, Poise, PoiseState, Pos, Stats, }, effect::Effect, lottery::Lottery, @@ -23,7 +23,7 @@ use common::{ terrain::{Block, TerrainGrid}, uid::{Uid, UidAllocator}, vol::ReadVol, - Damage, DamageSource, Explosion, GroupTarget, RadiusEffect, + Damage, DamageSource, Explosion, GroupTarget, Knockback, RadiusEffect, }; use common_net::{msg::ServerGeneral, sync::WorldSyncExt}; use common_sys::state::BlockChange; @@ -31,13 +31,89 @@ use comp::item::Reagent; use hashbrown::HashSet; use rand::prelude::*; use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt}; +use std::time::Duration; use tracing::error; use vek::Vec3; -pub fn handle_damage(server: &Server, entity: EcsEntity, change: HealthChange) { +pub fn handle_damage(server: &Server, entity: EcsEntity, change: (HealthChange, i32)) { let ecs = &server.state.ecs(); if let Some(mut health) = ecs.write_storage::().get_mut(entity) { health.change_by(change); + if let Some(poise) = ecs.write_storage::().get_mut(entity) { + poise.change_by(change.1); + let was_wielded = + if let Some(character_state) = ecs.read_storage::().get(entity) { + character_state.is_wield() + } else { + false + }; + match poise.poise_state() { + PoiseState::Normal => {}, + PoiseState::Interrupted => { + poise.reset(); + let _ = ecs.write_storage::().insert( + entity, + comp::CharacterState::Stunned(common::states::stunned::Data { + static_data: common::states::stunned::StaticData { + buildup_duration: Duration::from_millis(250), + recover_duration: Duration::from_millis(250), + knockback: Knockback::Away(0.0), + }, + timer: Duration::default(), + stage_section: common::states::utils::StageSection::Buildup, + was_wielded, + }), + ); + }, + PoiseState::Stunned => { + poise.reset(); + let _ = ecs.write_storage::().insert( + entity, + comp::CharacterState::Stunned(common::states::stunned::Data { + static_data: common::states::stunned::StaticData { + buildup_duration: Duration::from_millis(250), + recover_duration: Duration::from_millis(250), + knockback: Knockback::Away(0.0), + }, + timer: Duration::default(), + stage_section: common::states::utils::StageSection::Buildup, + was_wielded, + }), + ); + }, + PoiseState::Dazed => { + poise.reset(); + let _ = ecs.write_storage::().insert( + entity, + comp::CharacterState::Stunned(common::states::stunned::Data { + static_data: common::states::stunned::StaticData { + buildup_duration: Duration::from_millis(250), + recover_duration: Duration::from_millis(250), + knockback: Knockback::Away(0.0), + }, + timer: Duration::default(), + stage_section: common::states::utils::StageSection::Buildup, + was_wielded, + }), + ); + }, + PoiseState::KnockedDown => { + poise.reset(); + let _ = ecs.write_storage::().insert( + entity, + comp::CharacterState::Stunned(common::states::stunned::Data { + static_data: common::states::stunned::StaticData { + buildup_duration: Duration::from_millis(250), + recover_duration: Duration::from_millis(250), + knockback: Knockback::Away(0.0), + }, + timer: Duration::default(), + stage_section: common::states::utils::StageSection::Buildup, + was_wielded, + }), + ); + }, + } } } @@ -487,6 +563,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) let damage = Damage { source: DamageSource::Falling, value: falldmg, + poise_damage: 70.0, }; let inventories = state.ecs().read_storage::(); let change = damage.modify_damage(inventories.get(entity), None); diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index cd747048b9..6f2cd52d14 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -110,6 +110,7 @@ impl Server { pos, stats, health, + poise, loadout, body, agent, @@ -123,6 +124,7 @@ impl Server { pos, stats, health, + poise, loadout, body, agent, diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index bc8631ca16..9a92926740 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -103,6 +103,7 @@ impl<'a> System<'a> for Sys { comp::Body::Humanoid(_) => entity.get_loadout(), _ => LoadoutBuilder::new().build(), }, + poise: comp::Poise::new(body), body, agent: Some(comp::Agent::new( None, diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index cc5c98b296..fc4e10e28f 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -36,6 +36,7 @@ pub trait StateExt { stats: comp::Stats, health: comp::Health, inventory: comp::Inventory, + poise: comp::Poise, body: comp::Body, ) -> EcsEntityBuilder; /// Build a static object entity @@ -95,6 +96,12 @@ impl StateExt for State { .get_mut(entity) .map(|mut health| health.change_by(change)); }, + Effect::Poise(change) => { + self.ecs() + .write_storage::() + .get_mut(entity) + .map(|poise| poise.change_by(change)); + }, Effect::Buff(buff) => { self.ecs() .write_storage::() @@ -117,6 +124,7 @@ impl StateExt for State { stats: comp::Stats, health: comp::Health, inventory: comp::Inventory, + poise: comp::Poise, body: comp::Body, ) -> EcsEntityBuilder { self.ecs_mut() @@ -149,6 +157,7 @@ impl StateExt for State { )) .with(stats) .with(health) + .with(poise) .with(comp::Alignment::Npc) .with(comp::Gravity(1.0)) .with(comp::CharacterState::default()) @@ -292,6 +301,7 @@ impl StateExt for State { ); self.write_component(entity, comp::Health::new(body, health_level)); self.write_component(entity, comp::Energy::new(body, energy_level)); + self.write_component(entity, comp::Poise::new(body)); self.write_component(entity, stats); self.write_component(entity, inventory); self.write_component( diff --git a/server/src/sys/object.rs b/server/src/sys/object.rs index 332794a6b6..0bd31f55a4 100644 --- a/server/src/sys/object.rs +++ b/server/src/sys/object.rs @@ -54,6 +54,7 @@ impl<'a> System<'a> for Sys { Effect::Damage(Damage { source: DamageSource::Explosion, value: 500.0, + poise_damage: 60.0, }), ), RadiusEffect::TerrainDestruction(4.0), @@ -81,6 +82,7 @@ impl<'a> System<'a> for Sys { Effect::Damage(Damage { source: DamageSource::Explosion, value: 50.0, + poise_damage: 10.0, }), ), RadiusEffect::TerrainDestruction(4.0), diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index f3c5daee58..9bfe4b6451 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -2,8 +2,8 @@ use super::SysTimer; use common::{ comp::{ Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider, Energy, Gravity, - Group, Health, Inventory, Item, LightEmitter, Mass, MountState, Mounting, Ori, Player, Pos, - Scale, Shockwave, Stats, Sticky, Vel, + Group, Health, Inventory, Item, LightEmitter, Mass, MountState, Mounting, Ori, Player, + Poise, Pos, Scale, Shockwave, Stats, Sticky, Vel, }, span, uid::Uid, @@ -51,6 +51,7 @@ pub struct TrackedComps<'a> { pub auras: ReadStorage<'a, Auras>, pub energy: ReadStorage<'a, Energy>, pub health: ReadStorage<'a, Health>, + pub poise: ReadStorage<'a, Poise>, pub can_build: ReadStorage<'a, CanBuild>, pub light_emitter: ReadStorage<'a, LightEmitter>, pub item: ReadStorage<'a, Item>, @@ -179,6 +180,7 @@ pub struct ReadTrackers<'a> { pub auras: ReadExpect<'a, UpdateTracker>, pub energy: ReadExpect<'a, UpdateTracker>, pub health: ReadExpect<'a, UpdateTracker>, + pub poise: ReadExpect<'a, UpdateTracker>, pub can_build: ReadExpect<'a, UpdateTracker>, pub light_emitter: ReadExpect<'a, UpdateTracker>, pub inventory: ReadExpect<'a, UpdateTracker>, @@ -252,6 +254,7 @@ pub struct WriteTrackers<'a> { auras: WriteExpect<'a, UpdateTracker>, energy: WriteExpect<'a, UpdateTracker>, health: WriteExpect<'a, UpdateTracker>, + poise: WriteExpect<'a, UpdateTracker>, can_build: WriteExpect<'a, UpdateTracker>, light_emitter: WriteExpect<'a, UpdateTracker>, item: WriteExpect<'a, UpdateTracker>, @@ -319,6 +322,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { log_counts!(stats, "Stats"); log_counts!(energy, "Energies"); log_vounts!(health, "Healths"); + log_vounts!(poise, "Poises"); log_counts!(light_emitter, "Light emitters"); log_counts!(item, "Items"); log_counts!(scale, "Scales"); @@ -344,6 +348,7 @@ pub fn register_trackers(world: &mut World) { world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); + world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 1b6c2000f1..0f303be62a 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -152,6 +152,7 @@ impl<'a> System<'a> for Sys { LoadoutBuilder::build_loadout(body, main_tool, loadout_config).build(); let health = comp::Health::new(body, entity.level.unwrap_or(0)); + let poise = comp::Poise::new(stats.body_type); let can_speak = match body { comp::Body::Humanoid(_) => alignment == comp::Alignment::Npc, @@ -176,6 +177,7 @@ impl<'a> System<'a> for Sys { pos: Pos(entity.pos), stats, health, + poise, loadout, agent: if entity.has_agency { Some(comp::Agent::new(