Conversion of poise damage to health damage now scales depending on the poise state the target is in.

Entities are now immune to poise damage for 1 second after exiting a poise state.
Rebalanced most phyiscal damage attacks on player-accessible tools.
This commit is contained in:
Sam 2022-01-08 00:36:15 -05:00
parent 2e2e56661f
commit e98edbcc7a
16 changed files with 249 additions and 243 deletions

View File

@ -1,69 +1,56 @@
// ComboMelee(
// stage_data: [
// (
// stage: 1,
// base_damage: 11.0,
// base_poise_damage: 12,
// damage_increase: 1.0,
// poise_damage_increase: 0,
// knockback: 5.0,
// range: 3.5,
// angle: 50.0,
// base_buildup_duration: 0.15,
// base_swing_duration: 0.075,
// hit_timing: 0.6,
// base_recover_duration: 0.35,
// forward_movement: 0.5,
// damage_kind: Slashing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// (
// stage: 2,
// base_damage: 13.0,
// base_poise_damage: 20,
// damage_increase: 1.5,
// poise_damage_increase: 0,
// knockback: 6.0,
// range: 3.5,
// angle: 30.0,
// base_buildup_duration: 0.2,
// base_swing_duration: 0.1,
// hit_timing: 0.6,
// base_recover_duration: 0.35,
// forward_movement: 0.25,
// damage_kind: Slashing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// ],
// initial_energy_gain: 2.5,
// max_energy_gain: 17.5,
// energy_increase: 3.0,
// speed_increase: 0.1,
// max_speed_increase: 0.6,
// scales_from_combo: 2,
// is_interruptible: false,
// ori_modifier: 1.0,
// )
BasicMelee(
energy_cost: 0,
buildup_duration: 0.1,
swing_duration: 0.05,
recover_duration: 0.1,
base_damage: 10.0,
ComboMelee(
stage_data: [
(
stage: 1,
base_damage: 11.0,
damage_increase: 1.0,
base_poise_damage: 0,
knockback: ( strength: 0.0, direction: Away),
range: 5.0,
max_angle: 45.0,
damage_effect: None,
poise_damage_increase: 0,
knockback: 5.0,
range: 3.5,
angle: 50.0,
base_buildup_duration: 0.15,
base_swing_duration: 0.075,
hit_timing: 0.6,
base_recover_duration: 0.35,
forward_movement: 0.5,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
(
stage: 2,
base_damage: 13.0,
damage_increase: 1.5,
base_poise_damage: 0,
poise_damage_increase: 0,
knockback: 6.0,
range: 3.5,
angle: 30.0,
base_buildup_duration: 0.2,
base_swing_duration: 0.1,
hit_timing: 0.6,
base_recover_duration: 0.35,
forward_movement: 0.25,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
],
initial_energy_gain: 2.5,
max_energy_gain: 17.5,
energy_increase: 3.0,
speed_increase: 0.1,
max_speed_increase: 0.6,
scales_from_combo: 2,
is_interruptible: false,
ori_modifier: 1.0,
)

View File

@ -4,8 +4,8 @@ LeapMelee(
movement_duration: 0.2,
swing_duration: 0.2,
recover_duration: 0.2,
base_damage: 15.0,
base_poise_damage: 70,
base_damage: 30.0,
base_poise_damage: 0,
knockback: 12.0,
range: 4.5,
max_angle: 30.0,

View File

@ -2,8 +2,8 @@ SpinMelee(
buildup_duration: 0.2,
swing_duration: 0.6,
recover_duration: 0.2,
base_damage: 7.0,
base_poise_damage: 25,
base_damage: 8.0,
base_poise_damage: 0,
knockback: ( strength: 0.0, direction: Away),
range: 3.5,
damage_effect: Some(Buff((

View File

@ -4,7 +4,7 @@ ChargedRanged(
initial_regen: 0.5,
scaled_regen: 12.0,
initial_damage: 0.5,
scaled_damage: 14.0,
scaled_damage: 12.0,
initial_knockback: 0.0,
scaled_knockback: 10.0,
buildup_duration: 0.2,

View File

@ -1,10 +1,10 @@
ChargedMelee(
energy_cost: 1,
energy_drain: 30.0,
initial_damage: 1.0,
scaled_damage: 16.0,
initial_poise_damage: 5,
scaled_poise_damage: 75,
initial_damage: 0.0,
scaled_damage: 20.0,
initial_poise_damage: 0,
scaled_poise_damage: 30,
initial_knockback: 5.0,
scaled_knockback: 20.0,
range: 3.5,

View File

@ -4,8 +4,8 @@ LeapMelee(
movement_duration: 0.8,
swing_duration: 0.15,
recover_duration: 0.2,
base_damage: 16.0,
base_poise_damage: 80,
base_damage: 25.0,
base_poise_damage: 40,
knockback: 25.0,
range: 4.5,
max_angle: 360.0,

View File

@ -1,39 +1,26 @@
// ComboMelee(
// stage_data: [(
// stage: 1,
// base_damage: 15.0,
// damage_increase: 1.0,
// base_poise_damage: 20,
// poise_damage_increase: 0,
// knockback: 3.5,
// range: 4.5,
// angle: 50.0,
// base_buildup_duration: 0.2,
// base_swing_duration: 0.1,
// hit_timing: 0.5,
// base_recover_duration: 0.45,
// forward_movement: 0.0,
// damage_kind: Crushing,
// )],
// initial_energy_gain: 5.0,
// max_energy_gain: 15.0,
// energy_increase: 5.0,
// speed_increase: 0.1,
// max_speed_increase: 0.4,
// scales_from_combo: 2,
// is_interruptible: false,
// ori_modifier: 1.0,
// )
BasicMelee(
energy_cost: 0,
buildup_duration: 0.1,
swing_duration: 0.05,
recover_duration: 0.1,
base_damage: 10.0,
ComboMelee(
stage_data: [(
stage: 1,
base_damage: 15.0,
damage_increase: 1.0,
base_poise_damage: 0,
knockback: ( strength: 0.0, direction: Away),
range: 5.0,
max_angle: 45.0,
damage_effect: None,
poise_damage_increase: 0,
knockback: 3.5,
range: 4.5,
angle: 50.0,
base_buildup_duration: 0.2,
base_swing_duration: 0.1,
hit_timing: 0.5,
base_recover_duration: 0.45,
forward_movement: 0.0,
damage_kind: Crushing,
)],
initial_energy_gain: 5.0,
max_energy_gain: 15.0,
energy_increase: 5.0,
speed_increase: 0.1,
max_speed_increase: 0.4,
scales_from_combo: 2,
is_interruptible: false,
ori_modifier: 1.0,
)

View File

@ -3,7 +3,7 @@ DashMelee(
base_damage: 8.0,
scaled_damage: 16.0,
base_poise_damage: 0,
scaled_poise_damage: 60,
scaled_poise_damage: 0,
base_knockback: 8.0,
scaled_knockback: 7.0,
range: 4.0,

View File

@ -3,7 +3,7 @@ SpinMelee(
swing_duration: 0.4,
recover_duration: 0.5,
base_damage: 13.0,
base_poise_damage: 13,
base_poise_damage: 0,
knockback: ( strength: 10.0, direction: Away),
range: 3.5,
damage_effect: Some(Buff((

View File

@ -1,91 +1,78 @@
// ComboMelee(
// stage_data: [
// (
// stage: 1,
// base_damage: 10.0,
// damage_increase: 1.0,
// base_poise_damage: 10,
// poise_damage_increase: 0,
// knockback: 0.0,
// range: 4.0,
// angle: 30.0,
// base_buildup_duration: 0.1,
// base_swing_duration: 0.075,
// hit_timing: 0.5,
// base_recover_duration: 0.15,
// forward_movement: 0.5,
// damage_kind: Slashing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// (
// stage: 2,
// base_damage: 8.0,
// damage_increase: 1.5,
// base_poise_damage: 13,
// poise_damage_increase: 0,
// knockback: 2.0,
// range: 3.5,
// angle: 40.0,
// base_buildup_duration: 0.1,
// base_swing_duration: 0.1,
// hit_timing: 0.5,
// base_recover_duration: 0.3,
// forward_movement: 0.0,
// damage_kind: Slashing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// (
// stage: 3,
// base_damage: 13,
// damage_increase: 2,
// base_poise_damage: 15,
// poise_damage_increase: 0,
// knockback: 2.0,
// range: 6.0,
// angle: 10.0,
// base_buildup_duration: 0.15,
// base_swing_duration: 0.1,
// hit_timing: 0.2,
// base_recover_duration: 0.35,
// forward_movement: 1.2,
// damage_kind: Piercing,
// damage_effect: Some(Buff((
// kind: Bleeding,
// dur_secs: 10.0,
// strength: DamageFraction(0.1),
// chance: 0.1,
// ))),
// ),
// ],
// initial_energy_gain: 0,
// max_energy_gain: 20.0,
// energy_increase: 2.5,
// speed_increase: 0.1,
// max_speed_increase: 0.8,
// scales_from_combo: 2,
// is_interruptible: true,
// ori_modifier: 1.0,
// )
BasicMelee(
energy_cost: 0,
buildup_duration: 0.1,
swing_duration: 0.05,
recover_duration: 0.1,
ComboMelee(
stage_data: [
(
stage: 1,
base_damage: 10.0,
damage_increase: 1.0,
base_poise_damage: 0,
knockback: ( strength: 0.0, direction: Away),
range: 5.0,
max_angle: 45.0,
damage_effect: None,
poise_damage_increase: 0,
knockback: 0.0,
range: 4.0,
angle: 30.0,
base_buildup_duration: 0.1,
base_swing_duration: 0.075,
hit_timing: 0.5,
base_recover_duration: 0.15,
forward_movement: 0.5,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
(
stage: 2,
base_damage: 8.0,
damage_increase: 1.5,
base_poise_damage: 0,
poise_damage_increase: 0,
knockback: 2.0,
range: 3.5,
angle: 40.0,
base_buildup_duration: 0.1,
base_swing_duration: 0.1,
hit_timing: 0.5,
base_recover_duration: 0.3,
forward_movement: 0.0,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
(
stage: 3,
base_damage: 10.0,
damage_increase: 2,
base_poise_damage: 0,
poise_damage_increase: 0,
knockback: 2.0,
range: 6.0,
angle: 10.0,
base_buildup_duration: 0.15,
base_swing_duration: 0.1,
hit_timing: 0.2,
base_recover_duration: 0.35,
forward_movement: 1.2,
damage_kind: Piercing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
],
initial_energy_gain: 0,
max_energy_gain: 20.0,
energy_increase: 2.5,
speed_increase: 0.1,
max_speed_increase: 0.8,
scales_from_combo: 2,
is_interruptible: true,
ori_modifier: 1.0,
)

View File

@ -283,6 +283,7 @@ impl Attack {
impulse: *dir,
by: attacker.map(|x| x.into()),
cause: Some(damage.damage.source),
time,
};
if change.abs() > Poise::POISE_EPSILON {
emit(ServerEvent::PoiseChange {
@ -354,6 +355,7 @@ impl Attack {
impulse: *dir,
by: attacker.map(|x| x.into()),
cause: Some(damage.damage.source),
time,
};
emit(ServerEvent::PoiseChange {
entity: target.entity,
@ -496,6 +498,7 @@ impl Attack {
impulse: *dir,
by: attacker.map(|x| x.into()),
cause: Some(attack_source.into()),
time,
};
emit(ServerEvent::PoiseChange {
entity: target.entity,

View File

@ -5,6 +5,7 @@ use crate::{
inventory::item::{armor::Protection, ItemKind},
CharacterState, Inventory,
},
resources::Time,
states,
util::Dir,
};
@ -26,6 +27,8 @@ pub struct PoiseChange {
pub by: Option<DamageContributor>,
/// The category of action that resulted in the poise change
pub cause: Option<DamageSource>,
/// The time that the poise change occurred at
pub time: Time,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
@ -51,6 +54,8 @@ pub struct Poise {
pub last_change: Dir,
/// Rate of poise regeneration per tick. Starts at zero and accelerates.
pub regen_rate: f32,
/// Time that entity was last in a poise state
time_last_poise: Option<Time>,
}
/// States to define effects of a poise change
@ -69,7 +74,9 @@ pub enum PoiseState {
}
impl PoiseState {
pub fn poise_effect(&self, was_wielded: bool) -> (Option<CharacterState>, Option<f32>) {
/// Returns the optional stunned character state and duration of stun, and
/// optional impulse strength corresponding to a particular poise state
pub fn poise_effect(&self, was_wielded: bool) -> (Option<(CharacterState, f64)>, Option<f32>) {
use states::{
stunned::{Data, StaticData},
utils::StageSection,
@ -91,12 +98,13 @@ impl PoiseState {
None,
),
PoiseState::KnockedDown => (
Some((Duration::from_millis(900), Duration::from_millis(700), 0.0)),
Some((Duration::from_millis(1000), Duration::from_millis(600), 0.0)),
Some(10.0),
),
};
(
charstate_parameters.map(|(buildup_duration, recover_duration, movement_speed)| {
(
CharacterState::Stunned(Data {
static_data: StaticData {
buildup_duration,
@ -107,11 +115,26 @@ impl PoiseState {
timer: Duration::default(),
stage_section: StageSection::Buildup,
was_wielded,
})
}),
buildup_duration.as_secs_f64() + recover_duration.as_secs_f64(),
)
}),
impulse,
)
}
/// Returns the multiplier on poise damage to health damage for when the
/// target is in a poise state
pub fn damage_multiplier(&self) -> f32 {
match self {
Self::Interrupted => 0.1,
Self::Stunned => 0.25,
Self::Dazed => 0.5,
Self::KnockedDown => 1.0,
// Should never be reached
Self::Normal => 0.0,
}
}
}
impl Poise {
@ -122,6 +145,9 @@ impl Poise {
/// can fit into an f32 with no loss to precision
// Cast to u32 done as u32::from cannot be called inside constant
const MAX_SCALED_POISE: u32 = Self::MAX_POISE as u32 * Self::SCALING_FACTOR_INT;
/// The amount of time after being in a poise state before you can take
/// poise damage again
const POISE_BUFFER_TIME: f64 = 1.0;
/// Used when comparisons to poise are needed outside this module.
// This value is chosen as anything smaller than this is more precise than our
// units of poise.
@ -161,17 +187,27 @@ impl Poise {
maximum: poise,
last_change: Dir::default(),
regen_rate: 0.0,
time_last_poise: None,
}
}
pub fn change(&mut self, change: PoiseChange) {
self.current = (((self.current() + change.amount).clamp(0.0, f32::from(Self::MAX_POISE))
match self.time_last_poise {
Some(last_time) if last_time.0 + Poise::POISE_BUFFER_TIME > change.time.0 => {},
_ => {
self.current = (((self.current() + change.amount)
.clamp(0.0, f32::from(Self::MAX_POISE))
* Self::SCALING_FACTOR_FLOAT) as u32)
.min(self.maximum);
self.last_change = Dir::from_unnormalized(change.impulse).unwrap_or_default();
},
}
}
pub fn reset(&mut self) { self.current = self.maximum; }
pub fn reset(&mut self, time: Time, poise_state_time: f64) {
self.current = self.maximum;
self.time_last_poise = Some(Time(time.0 + poise_state_time));
}
/// Returns knockback as a Dir
/// Kept as helper function should additional fields ever be added to last

View File

@ -12,7 +12,7 @@ use common::{
},
event::{EventBus, LocalEvent, ServerEvent},
outcome::Outcome,
resources::DeltaTime,
resources::{DeltaTime, Time},
states::{
behavior::{JoinData, JoinStruct},
idle,
@ -28,6 +28,7 @@ pub struct ReadData<'a> {
server_bus: Read<'a, EventBus<ServerEvent>>,
local_bus: Read<'a, EventBus<LocalEvent>>,
dt: Read<'a, DeltaTime>,
time: Read<'a, Time>,
lazy_update: Read<'a, LazyUpdate>,
healths: ReadStorage<'a, Health>,
bodies: ReadStorage<'a, Body>,
@ -146,11 +147,11 @@ impl<'a> System<'a> for Sys {
let was_wielded = char_state.is_wield();
let poise_state = poise.poise_state();
let pos = pos.0;
if let (Some(stunned_state), impulse_strength) =
if let (Some((stunned_state, stunned_duration)), impulse_strength) =
poise_state.poise_effect(was_wielded)
{
// Reset poise if there is some stunned state to apply
poise.reset();
poise.reset(*read_data.time, stunned_duration);
*char_state = stunned_state;
outcomes.push(Outcome::PoiseChange {
pos,

View File

@ -178,6 +178,7 @@ impl<'a> System<'a> for Sys {
impulse: Vec3::zero(),
by: None,
cause: None,
time: *read_data.time,
};
poise.change(poise_change);
poise.regen_rate = (poise.regen_rate + POISE_REGEN_ACCEL * dt).min(10.0);

View File

@ -53,15 +53,11 @@ pub fn handle_poise(server: &Server, entity: EcsEntity, change: comp::PoiseChang
if let Some(character_state) = ecs.read_storage::<CharacterState>().get(entity) {
// Entity is invincible to poise change during stunned/staggered character
// state, but the mitigated poise damage is converted to health damage instead
if !character_state.is_stunned() {
if let Some(mut poise) = ecs.write_storage::<Poise>().get_mut(entity) {
poise.change(change);
}
} else {
// TODO: Look into multiplying health by some fraction dependent on poise state
if let CharacterState::Stunned(data) = character_state {
let health_change = change.amount * data.static_data.poise_state.damage_multiplier();
let time = ecs.read_resource::<Time>();
let health_change = HealthChange {
amount: change.amount,
amount: health_change,
by: None,
cause: None,
time: *time,
@ -71,6 +67,8 @@ pub fn handle_poise(server: &Server, entity: EcsEntity, change: comp::PoiseChang
entity,
change: health_change,
});
} else if let Some(mut poise) = ecs.write_storage::<Poise>().get_mut(entity) {
poise.change(change);
}
}
}
@ -527,6 +525,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
let inventories = ecs.read_storage::<Inventory>();
let stats = ecs.read_storage::<Stats>();
let time = server.state.ecs().read_resource::<Time>();
// Handle health change
if let Some(mut health) = ecs.write_storage::<comp::Health>().get_mut(entity) {
@ -540,7 +539,6 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
inventories.get(entity),
stats.get(entity),
);
let time = server.state.ecs().read_resource::<Time>();
let change =
damage.calculate_health_change(damage_reduction, None, false, 0.0, 1.0, *time);
health.change_by(change);
@ -554,6 +552,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
impulse: Vec3::unit_z(),
by: None,
cause: None,
time: *time,
};
poise.change(poise_change);
}
@ -1217,6 +1216,7 @@ pub fn handle_entity_attacked_hook(server: &Server, entity: EcsEntity) {
let ecs = &server.state.ecs();
let server_eventbus = ecs.read_resource::<EventBus<ServerEvent>>();
let mut outcomes = ecs.write_resource::<Vec<Outcome>>();
let time = ecs.read_resource::<Time>();
if let (Some(mut char_state), Some(mut poise), Some(pos)) = (
ecs.write_storage::<CharacterState>().get_mut(entity),
@ -1228,11 +1228,13 @@ pub fn handle_entity_attacked_hook(server: &Server, entity: EcsEntity) {
*char_state,
CharacterState::SpriteInteract(_) | CharacterState::UseItem(_)
) {
let poise_state = comp::poise::PoiseState::Dazed;
let poise_state = comp::poise::PoiseState::Interrupted;
let was_wielded = char_state.is_wield();
if let (Some(stunned_state), impulse_strength) = poise_state.poise_effect(was_wielded) {
if let (Some((stunned_state, stunned_duration)), impulse_strength) =
poise_state.poise_effect(was_wielded)
{
// Reset poise if there is some stunned state to apply
poise.reset();
poise.reset(*time, stunned_duration);
*char_state = stunned_state;
outcomes.push(Outcome::PoiseChange {
pos: pos.0,

View File

@ -170,11 +170,13 @@ impl StateExt for State {
DamageContributor::new(uid, groups.get(attacker_entity).cloned())
})
});
let time = self.ecs().read_resource::<Time>();
let poise_change = comp::PoiseChange {
amount: change,
impulse: Vec3::zero(),
cause: None,
by: damage_contributor,
time: *time,
};
self.ecs()
.write_storage::<comp::Poise>()