Heavy stance required abilities

This commit is contained in:
Sam 2022-12-15 21:51:03 -05:00
parent e73236819a
commit a3d655970e
28 changed files with 277 additions and 219 deletions

View File

@ -1,39 +1,9 @@
ComboMelee2( SelfBuff(
strikes: [ buildup_duration: 0.2,
( cast_duration: 0.2,
melee_constructor: ( recover_duration: 0.6,
kind: Slash( buff_kind: Fortitude,
damage: 5, buff_strength: 1.0,
poise: 0, buff_duration: Some(30.0),
knockback: 0, energy_cost: 25,
energy_regen: 5,
),
range: 3.0,
angle: 45.0,
),
buildup_duration: 0.15,
swing_duration: 0.05,
hit_timing: 0.5,
recover_duration: 0.1,
ori_modifier: 0.6,
),
(
melee_constructor: (
kind: Slash(
damage: 10,
poise: 0,
knockback: 0,
energy_regen: 7.5,
),
range: 3.0,
angle: 45.0,
),
buildup_duration: 0.1,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.2,
ori_modifier: 0.6,
),
],
energy_cost_per_strike: 0,
) )

View File

@ -1,39 +1,26 @@
ComboMelee2( DiveMelee(
strikes: [ energy_cost: 25,
( vertical_speed: 5,
melee_constructor: ( buildup_duration: Some(0.2),
kind: Slash( movement_duration: 5,
damage: 5, swing_duration: 0.1,
poise: 0, recover_duration: 0.3,
knockback: 0, melee_constructor: (
energy_regen: 5, kind: Slash(
), damage: 30,
range: 3.0, poise: 40,
angle: 45.0, knockback: 0,
), energy_regen: 0,
buildup_duration: 0.15,
swing_duration: 0.05,
hit_timing: 0.5,
recover_duration: 0.1,
ori_modifier: 0.6,
), ),
( scaled: Some(Slash(
melee_constructor: ( damage: 5,
kind: Slash( poise: 10,
damage: 10, knockback: 0,
poise: 0, energy_regen: 0,
knockback: 0, )),
energy_regen: 7.5, range: 2.0,
), angle: 45.0,
range: 3.0, multi_target: Some(Normal),
angle: 45.0, ),
), max_scaling: 6,
buildup_duration: 0.1,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.2,
ori_modifier: 0.6,
),
],
energy_cost_per_strike: 0,
) )

View File

@ -63,7 +63,7 @@ buff-title-ensnared = Ensnared
buff-desc-ensnared = Vines grasp at your legs, impeding your movement. buff-desc-ensnared = Vines grasp at your legs, impeding your movement.
## Fortitude ## Fortitude
buff-title-fortitude = Fortitude buff-title-fortitude = Fortitude
buff-desc-fortitude = You can withstand staggers. buff-desc-fortitude = You can withstand staggers, and as you take more damage you stagger others more easily.
## Parried ## Parried
buff-title-parried = Parried buff-title-parried = Parried
buff-desc-parried = You were parried and now are slow to recover. buff-desc-parried = You were parried and now are slow to recover.

View File

@ -56,6 +56,7 @@ pub struct AttackerInfo<'a> {
pub energy: Option<&'a Energy>, pub energy: Option<&'a Energy>,
pub combo: Option<&'a Combo>, pub combo: Option<&'a Combo>,
pub inventory: Option<&'a Inventory>, pub inventory: Option<&'a Inventory>,
pub stats: Option<&'a Stats>,
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -294,7 +295,11 @@ impl Attack {
// that health change amount is greater than 0 would fail. // that health change amount is greater than 0 would fail.
let reduced_damage = let reduced_damage =
applied_damage * damage_reduction / (1.0 - damage_reduction); applied_damage * damage_reduction / (1.0 - damage_reduction);
let poise = reduced_damage * CRUSHING_POISE_FRACTION; let poise = reduced_damage
* CRUSHING_POISE_FRACTION
* attacker
.and_then(|a| a.stats)
.map_or(1.0, |s| s.poise_damage_modifier);
let change = -Poise::apply_poise_reduction( let change = -Poise::apply_poise_reduction(
poise, poise,
target.inventory, target.inventory,
@ -369,6 +374,7 @@ impl Attack {
time, time,
attacker.map(|a| a.uid), attacker.map(|a| a.uid),
target.stats, target.stats,
target.health,
applied_damage, applied_damage,
strength_modifier, strength_modifier,
)), )),
@ -401,7 +407,10 @@ impl Attack {
msm, msm,
target.char_state, target.char_state,
target.stats, target.stats,
) * strength_modifier; ) * strength_modifier
* attacker
.and_then(|a| a.stats)
.map_or(1.0, |s| s.poise_damage_modifier);
if change.abs() > Poise::POISE_EPSILON { if change.abs() > Poise::POISE_EPSILON {
let poise_change = PoiseChange { let poise_change = PoiseChange {
amount: change, amount: change,
@ -561,6 +570,7 @@ impl Attack {
time, time,
attacker.map(|a| a.uid), attacker.map(|a| a.uid),
target.stats, target.stats,
target.health,
accumulated_damage, accumulated_damage,
strength_modifier, strength_modifier,
)), )),
@ -593,7 +603,10 @@ impl Attack {
msm, msm,
target.char_state, target.char_state,
target.stats, target.stats,
) * strength_modifier; ) * strength_modifier
* attacker
.and_then(|a| a.stats)
.map_or(1.0, |s| s.poise_damage_modifier);
if change.abs() > Poise::POISE_EPSILON { if change.abs() > Poise::POISE_EPSILON {
let poise_change = PoiseChange { let poise_change = PoiseChange {
amount: change, amount: change,
@ -1082,7 +1095,8 @@ impl CombatBuff {
self, self,
time: Time, time: Time,
uid: Option<Uid>, uid: Option<Uid>,
stats: Option<&Stats>, tgt_stats: Option<&Stats>,
tgt_health: Option<&Health>,
damage: f32, damage: f32,
strength_modifier: f32, strength_modifier: f32,
) -> Buff { ) -> Buff {
@ -1102,7 +1116,8 @@ impl CombatBuff {
Vec::new(), Vec::new(),
source, source,
time, time,
stats, tgt_stats,
tgt_health,
) )
} }
} }

View File

@ -777,6 +777,7 @@ pub enum CharacterAbility {
DiveMelee { DiveMelee {
energy_cost: f32, energy_cost: f32,
vertical_speed: f32, vertical_speed: f32,
buildup_duration: Option<f32>,
movement_duration: f32, movement_duration: f32,
swing_duration: f32, swing_duration: f32,
recover_duration: f32, recover_duration: f32,
@ -893,11 +894,15 @@ impl CharacterAbility {
&& update.energy.try_change_by(-*energy_cost).is_ok() && update.energy.try_change_by(-*energy_cost).is_ok()
}, },
CharacterAbility::DiveMelee { CharacterAbility::DiveMelee {
buildup_duration,
energy_cost, energy_cost,
vertical_speed, vertical_speed,
.. ..
} => { } => {
data.vel.0.z < -*vertical_speed // If either falling fast enough or is on ground and able to be activated from
// ground
(data.vel.0.z < -*vertical_speed
|| (data.physics.on_ground.is_some() && buildup_duration.is_some()))
&& update.energy.try_change_by(-*energy_cost).is_ok() && update.energy.try_change_by(-*energy_cost).is_ok()
}, },
CharacterAbility::ComboMelee { .. } CharacterAbility::ComboMelee { .. }
@ -1425,12 +1430,14 @@ impl CharacterAbility {
ref mut energy_cost, ref mut energy_cost,
vertical_speed: _, vertical_speed: _,
movement_duration: _, movement_duration: _,
ref mut buildup_duration,
ref mut swing_duration, ref mut swing_duration,
ref mut recover_duration, ref mut recover_duration,
ref mut melee_constructor, ref mut melee_constructor,
max_scaling: _, max_scaling: _,
meta: _, meta: _,
} => { } => {
*buildup_duration = buildup_duration.map(|b| b / stats.speed);
*swing_duration /= stats.speed; *swing_duration /= stats.speed;
*recover_duration /= stats.speed; *recover_duration /= stats.speed;
*energy_cost /= stats.energy_efficiency; *energy_cost /= stats.energy_efficiency;
@ -2744,6 +2751,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
exhausted: false, exhausted: false,
}), }),
CharacterAbility::DiveMelee { CharacterAbility::DiveMelee {
buildup_duration,
movement_duration, movement_duration,
swing_duration, swing_duration,
recover_duration, recover_duration,
@ -2754,6 +2762,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
meta: _, meta: _,
} => CharacterState::DiveMelee(dive_melee::Data { } => CharacterState::DiveMelee(dive_melee::Data {
static_data: dive_melee::StaticData { static_data: dive_melee::StaticData {
buildup_duration: buildup_duration.map(|b| Duration::from_secs_f32(b)),
movement_duration: Duration::from_secs_f32(*movement_duration), movement_duration: Duration::from_secs_f32(*movement_duration),
swing_duration: Duration::from_secs_f32(*swing_duration), swing_duration: Duration::from_secs_f32(*swing_duration),
recover_duration: Duration::from_secs_f32(*recover_duration), recover_duration: Duration::from_secs_f32(*recover_duration),
@ -2763,7 +2772,11 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
ability_info, ability_info,
}, },
timer: Duration::default(), timer: Duration::default(),
stage_section: StageSection::Movement, stage_section: if data.vel.0.z < 0.0 || buildup_duration.is_none() {
StageSection::Movement
} else {
StageSection::Buildup
},
exhausted: false, exhausted: false,
max_vertical_speed: 0.0, max_vertical_speed: 0.0,
}), }),

View File

@ -1,6 +1,6 @@
#![allow(clippy::nonstandard_macro_braces)] //tmp as of false positive !? #![allow(clippy::nonstandard_macro_braces)] //tmp as of false positive !?
use crate::{ use crate::{
comp::{aura::AuraKey, Stats}, comp::{aura::AuraKey, Health, Stats},
resources::{Secs, Time}, resources::{Secs, Time},
uid::Uid, uid::Uid,
}; };
@ -56,10 +56,13 @@ pub enum BuffKind {
/// Strength scales strength of both effects linearly. 0.5 is a 50% /// Strength scales strength of both effects linearly. 0.5 is a 50%
/// increase, 1.0 is a 100% increase. /// increase, 1.0 is a 100% increase.
Hastened, Hastened,
// TODO: Consider non linear scaling? /// Increases resistance to incoming poise, and poise damage dealt as health
/// Increases resistance to incoming poise over time /// is lost from the time the buff activated
/// Strength scales the resistance linearly, values over 1 will usually do /// Strength scales the resistance non-linearly. 0.5 provides 50%, 1.0
/// nothing. 0.5 is 50%, 1.0 is 100%. /// provides 67%
/// Strength scales the poise damage increase linearly, a strength of 1.0
/// and n health less from activation will cause poise damage to increase by
/// n%
Fortitude, Fortitude,
// Debuffs // Debuffs
/// Does damage to a creature over time /// Does damage to a creature over time
@ -214,6 +217,8 @@ pub enum BuffEffect {
PoiseReduction(f32), PoiseReduction(f32),
/// Reduces amount healed by consumables /// Reduces amount healed by consumables
HealReduction(f32), HealReduction(f32),
/// Increases poise damage dealt when health is lost
PoiseDamageFromLostHealth { initial_health: f32, strength: f32 },
} }
/// Actual de/buff. /// Actual de/buff.
@ -272,6 +277,7 @@ impl Buff {
source: BuffSource, source: BuffSource,
time: Time, time: Time,
stats: Option<&Stats>, stats: Option<&Stats>,
health: Option<&Health>,
) -> Self { ) -> Self {
// Normalized nonlinear scaling // Normalized nonlinear scaling
let nn_scaling = |a| a / (a + 0.5); let nn_scaling = |a| a / (a + 0.5);
@ -367,7 +373,13 @@ impl Buff {
BuffEffect::MovementSpeed(1.0 + data.strength), BuffEffect::MovementSpeed(1.0 + data.strength),
BuffEffect::AttackSpeed(1.0 + data.strength), BuffEffect::AttackSpeed(1.0 + data.strength),
], ],
BuffKind::Fortitude => vec![BuffEffect::PoiseReduction(data.strength)], BuffKind::Fortitude => vec![
BuffEffect::PoiseReduction(nn_scaling(data.strength)),
BuffEffect::PoiseDamageFromLostHealth {
initial_health: health.map_or(0.0, |h| h.current()),
strength: data.strength,
},
],
BuffKind::Parried => vec![BuffEffect::AttackSpeed(0.5)], BuffKind::Parried => vec![BuffEffect::AttackSpeed(0.5)],
BuffKind::PotionSickness => vec![BuffEffect::HealReduction(data.strength)], BuffKind::PotionSickness => vec![BuffEffect::HealReduction(data.strength)],
}; };

View File

@ -56,6 +56,7 @@ pub struct Stats {
pub attack_speed_modifier: f32, pub attack_speed_modifier: f32,
pub friction_modifier: f32, pub friction_modifier: f32,
pub max_energy_modifiers: StatsModifier, pub max_energy_modifiers: StatsModifier,
pub poise_damage_modifier: f32,
} }
impl Stats { impl Stats {
@ -70,6 +71,7 @@ impl Stats {
attack_speed_modifier: 1.0, attack_speed_modifier: 1.0,
friction_modifier: 1.0, friction_modifier: 1.0,
max_energy_modifiers: StatsModifier::default(), max_energy_modifiers: StatsModifier::default(),
poise_damage_modifier: 1.0,
} }
} }
@ -79,14 +81,8 @@ impl Stats {
/// Resets temporary modifiers to default values /// Resets temporary modifiers to default values
pub fn reset_temp_modifiers(&mut self) { pub fn reset_temp_modifiers(&mut self) {
self.damage_reduction = 0.0; // TODO: Figure out how to avoid clone
self.poise_reduction = 0.0; *self = Self::new(self.name.clone());
self.heal_multiplier = 1.0;
self.max_health_modifiers = StatsModifier::default();
self.move_speed_modifier = 1.0;
self.attack_speed_modifier = 1.0;
self.friction_modifier = 1.0;
self.max_energy_modifiers = StatsModifier::default();
} }
} }

View File

@ -146,7 +146,6 @@ pub struct JoinData<'a> {
pub terrain: &'a TerrainGrid, pub terrain: &'a TerrainGrid,
pub mount_data: Option<&'a Is<Rider>>, pub mount_data: Option<&'a Is<Rider>>,
pub stance: Option<&'a Stance>, pub stance: Option<&'a Stance>,
pub time: &'a Time,
} }
pub struct JoinStruct<'a> { pub struct JoinStruct<'a> {
@ -174,7 +173,6 @@ pub struct JoinStruct<'a> {
pub terrain: &'a TerrainGrid, pub terrain: &'a TerrainGrid,
pub mount_data: Option<&'a Is<Rider>>, pub mount_data: Option<&'a Is<Rider>>,
pub stance: Option<&'a Stance>, pub stance: Option<&'a Stance>,
pub time: &'a Time,
} }
impl<'a> JoinData<'a> { impl<'a> JoinData<'a> {
@ -216,7 +214,6 @@ impl<'a> JoinData<'a> {
active_abilities: j.active_abilities, active_abilities: j.active_abilities,
mount_data: j.mount_data, mount_data: j.mount_data,
stance: j.stance, stance: j.stance,
time: j.time,
} }
} }
} }

View File

@ -18,11 +18,9 @@ pub struct StaticData {
pub energy_drain: f32, pub energy_drain: f32,
/// Energy cost per attack /// Energy cost per attack
pub energy_cost: f32, pub energy_cost: f32,
/// The state can optionally have a buildup strike that applies after buildup before charging /// The state can optionally have a buildup strike that applies after
pub buildup_strike: Option<( /// buildup before charging
Duration, pub buildup_strike: Option<(Duration, MeleeConstructor)>,
MeleeConstructor,
)>,
/// How long it takes to charge the weapon to max damage and knockback /// How long it takes to charge the weapon to max damage and knockback
pub charge_duration: Duration, pub charge_duration: Duration,
/// How long the weapon is swinging for /// How long the weapon is swinging for
@ -74,10 +72,8 @@ impl CharacterBehavior for Data {
} else { } else {
let crit_data = get_crit_data(data, self.static_data.ability_info); let crit_data = get_crit_data(data, self.static_data.ability_info);
let buff_strength = get_buff_strength(data, self.static_data.ability_info); let buff_strength = get_buff_strength(data, self.static_data.ability_info);
data.updater.insert( data.updater
data.entity, .insert(data.entity, strike.create_melee(crit_data, buff_strength));
strike.create_melee(crit_data, buff_strength),
);
if let CharacterState::ChargedMelee(c) = &mut update.character { if let CharacterState::ChargedMelee(c) = &mut update.character {
c.stage_section = StageSection::Charge; c.stage_section = StageSection::Charge;

View File

@ -76,7 +76,8 @@ pub struct StaticData {
pub strikes: Vec<Strike<Duration>>, pub strikes: Vec<Strike<Duration>>,
/// The amount of energy consumed with each swing /// The amount of energy consumed with each swing
pub energy_cost_per_strike: f32, pub energy_cost_per_strike: f32,
/// Whether or not the state should progress through all strikes automatically once the state is entered /// Whether or not the state should progress through all strikes
/// automatically once the state is entered
pub auto_progress: bool, pub auto_progress: bool,
pub ability_info: AbilityInfo, pub ability_info: AbilityInfo,
} }
@ -133,7 +134,9 @@ impl CharacterBehavior for Data {
if let Some(movement) = strike_data.movement.swing { if let Some(movement) = strike_data.movement.swing {
handle_forced_movement(data, &mut update, movement); handle_forced_movement(data, &mut update, movement);
} }
if input_is_pressed(data, self.static_data.ability_info.input) || self.static_data.auto_progress { if input_is_pressed(data, self.static_data.ability_info.input)
|| self.static_data.auto_progress
{
if let CharacterState::ComboMelee2(c) = &mut update.character { if let CharacterState::ComboMelee2(c) = &mut update.character {
// Only have the next strike skip the recover period of this strike if not // Only have the next strike skip the recover period of this strike if not
// every strike in the combo is complete yet // every strike in the combo is complete yet

View File

@ -11,6 +11,8 @@ use std::time::Duration;
/// Separated out to condense update portions of character state /// Separated out to condense update portions of character state
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData { pub struct StaticData {
/// If Some(_), the state can be activated on the ground
pub buildup_duration: Option<Duration>,
/// How long the state is moving /// How long the state is moving
pub movement_duration: Duration, pub movement_duration: Duration,
/// How long the weapon swings /// How long the weapon swings
@ -51,6 +53,28 @@ impl CharacterBehavior for Data {
} }
match self.stage_section { match self.stage_section {
StageSection::Buildup => {
handle_orientation(data, &mut update, 0.5, None);
handle_move(data, &mut update, 0.3);
if let Some(buildup_duration) = self.static_data.buildup_duration {
if self.timer < buildup_duration {
if let CharacterState::DiveMelee(c) = &mut update.character {
c.timer = tick_attack_or_default(data, self.timer, None);
}
} else {
if let CharacterState::DiveMelee(c) = &mut update.character {
c.timer = Duration::default();
c.stage_section = StageSection::Action;
}
}
} else {
if let CharacterState::DiveMelee(c) = &mut update.character {
c.timer = Duration::default();
c.stage_section = StageSection::Action;
}
}
},
StageSection::Movement => { StageSection::Movement => {
handle_move(data, &mut update, 1.0); handle_move(data, &mut update, 1.0);
if data.physics.on_ground.is_some() { if data.physics.on_ground.is_some() {

View File

@ -92,9 +92,9 @@ impl CharacterBehavior for Data {
Some(max) => self.current_strike < max, Some(max) => self.current_strike < max,
None => input_is_pressed(data, self.static_data.ability_info.input), None => input_is_pressed(data, self.static_data.ability_info.input),
} && update } && update
.energy .energy
.try_change_by(-self.static_data.energy_cost) .try_change_by(-self.static_data.energy_cost)
.is_ok() .is_ok()
{ {
if let CharacterState::RapidMelee(c) = &mut update.character { if let CharacterState::RapidMelee(c) = &mut update.character {
c.timer = Duration::default(); c.timer = Duration::default();

View File

@ -72,6 +72,7 @@ impl CharacterBehavior for Data {
BuffSource::Character { by: *data.uid }, BuffSource::Character { by: *data.uid },
*data.time, *data.time,
Some(data.stats), Some(data.stats),
data.health,
); );
output_events.emit_server(ServerEvent::Buff { output_events.emit_server(ServerEvent::Buff {
entity: data.entity, entity: data.entity,

View File

@ -2,7 +2,7 @@ use crate::{
astar::Astar, astar::Astar,
combat, combat,
comp::{ comp::{
ability::{Ability, AbilityInput, AbilityMeta, Capability, AbilityInitEvent}, ability::{Ability, AbilityInitEvent, AbilityInput, AbilityMeta, Capability},
arthropod, biped_large, biped_small, bird_medium, arthropod, biped_large, biped_small, bird_medium,
character_state::OutputEvents, character_state::OutputEvents,
controller::InventoryManip, controller::InventoryManip,
@ -471,7 +471,9 @@ pub fn handle_forced_movement(
data.inputs.move_dir.reflected(Vec2::from(*data.ori)) data.inputs.move_dir.reflected(Vec2::from(*data.ori))
} else { } else {
data.inputs.move_dir data.inputs.move_dir
}.try_normalized().unwrap_or_else(|| -Vec2::from(*data.ori)); }
.try_normalized()
.unwrap_or_else(|| -Vec2::from(*data.ori));
update.vel.0 += direction * strength * accel * data.dt.0; update.vel.0 += direction * strength * accel * data.dt.0;
} }
}, },
@ -485,7 +487,9 @@ pub fn handle_forced_movement(
data.inputs.move_dir.reflected(Vec2::from(*data.ori)) data.inputs.move_dir.reflected(Vec2::from(*data.ori))
} else { } else {
data.inputs.move_dir data.inputs.move_dir
}.try_normalized().unwrap_or_else(|| Vec2::from(*data.ori)); }
.try_normalized()
.unwrap_or_else(|| Vec2::from(*data.ori));
let direction = direction.reflected(Vec2::from(*data.ori).rotated_z(PI / 2.)); let direction = direction.reflected(Vec2::from(*data.ori).rotated_z(PI / 2.));
update.vel.0 += direction * strength * accel * data.dt.0; update.vel.0 += direction * strength * accel * data.dt.0;
} }
@ -1060,7 +1064,12 @@ pub fn handle_jump(
.is_some() .is_some()
} }
fn handle_ability(data: &JoinData<'_>, update: &mut StateUpdate, output_events: &mut OutputEvents, input: InputKind) -> bool { fn handle_ability(
data: &JoinData<'_>,
update: &mut StateUpdate,
output_events: &mut OutputEvents,
input: InputKind,
) -> bool {
let context = AbilityContext::from(data.stance); let context = AbilityContext::from(data.stance);
if let Some(ability_input) = input.into() { if let Some(ability_input) = input.into() {
if let Some((ability, from_offhand)) = data if let Some((ability, from_offhand)) = data
@ -1084,7 +1093,10 @@ fn handle_ability(data: &JoinData<'_>, update: &mut StateUpdate, output_events:
if let Some(init_event) = ability.ability_meta().init_event { if let Some(init_event) = ability.ability_meta().init_event {
match init_event { match init_event {
AbilityInitEvent::EnterStance(stance) => { AbilityInitEvent::EnterStance(stance) => {
output_events.emit_server(ServerEvent::ChangeStance { entity: data.entity, stance }); output_events.emit_server(ServerEvent::ChangeStance {
entity: data.entity,
stance,
});
}, },
} }
} }
@ -1428,7 +1440,12 @@ impl AbilityInfo {
tool, tool,
hand, hand,
input, input,
input_attr: data.controller.queued_inputs.get(&input).map(|x| x.1).or_else(|| data.controller.held_inputs.get(&input).copied()), input_attr: data
.controller
.queued_inputs
.get(&input)
.map(|x| x.1)
.or_else(|| data.controller.held_inputs.get(&input).copied()),
ability_meta, ability_meta,
ability, ability,
} }

View File

@ -241,6 +241,7 @@ fn activate_aura(
source, source,
*read_data.time, *read_data.time,
stats, stats,
Some(health),
)), )),
}); });
} }

View File

@ -215,6 +215,7 @@ impl<'a> System<'a> for Sys {
energy: read_data.energies.get(entity), energy: read_data.energies.get(entity),
combo: read_data.combos.get(entity), combo: read_data.combos.get(entity),
inventory: read_data.inventories.get(entity), inventory: read_data.inventories.get(entity),
stats: read_data.stats.get(entity),
} }
}); });

View File

@ -151,6 +151,7 @@ impl<'a> System<'a> for Sys {
BuffSource::World, BuffSource::World,
*read_data.time, *read_data.time,
Some(&stat), Some(&stat),
Some(&health),
)), )),
}); });
} }
@ -168,6 +169,7 @@ impl<'a> System<'a> for Sys {
BuffSource::World, BuffSource::World,
*read_data.time, *read_data.time,
Some(&stat), Some(&stat),
Some(&health),
)), )),
}); });
} }
@ -185,6 +187,7 @@ impl<'a> System<'a> for Sys {
BuffSource::World, BuffSource::World,
*read_data.time, *read_data.time,
Some(&stat), Some(&stat),
Some(&health),
)), )),
}); });
// When standing on IceSpike also apply Frozen // When standing on IceSpike also apply Frozen
@ -197,6 +200,7 @@ impl<'a> System<'a> for Sys {
BuffSource::World, BuffSource::World,
*read_data.time, *read_data.time,
Some(&stat), Some(&stat),
Some(&health),
)), )),
}); });
} }
@ -217,6 +221,7 @@ impl<'a> System<'a> for Sys {
BuffSource::World, BuffSource::World,
*read_data.time, *read_data.time,
Some(&stat), Some(&stat),
Some(&health),
)), )),
}); });
} else if matches!( } else if matches!(
@ -296,6 +301,7 @@ impl<'a> System<'a> for Sys {
buff.source, buff.source,
*read_data.time, *read_data.time,
Some(&stat), Some(&stat),
Some(&health),
)), )),
}); });
} }
@ -573,5 +579,12 @@ fn execute_effect(
BuffEffect::HealReduction(red) => { BuffEffect::HealReduction(red) => {
stat.heal_multiplier *= 1.0 - *red; stat.heal_multiplier *= 1.0 - *red;
}, },
BuffEffect::PoiseDamageFromLostHealth {
initial_health,
strength,
} => {
let lost_health = (*initial_health - health.current()).max(0.0);
stat.poise_damage_modifier *= lost_health / 100.0 * *strength;
},
}; };
} }

View File

@ -201,7 +201,6 @@ impl<'a> System<'a> for Sys {
terrain: &read_data.terrain, terrain: &read_data.terrain,
mount_data: read_data.is_riders.get(entity), mount_data: read_data.is_riders.get(entity),
stance: read_data.stances.get(entity), stance: read_data.stances.get(entity),
time: &read_data.time,
}; };
for action in actions { for action in actions {
@ -274,7 +273,7 @@ impl Sys {
for (input, attr) in state_update.queued_inputs { for (input, attr) in state_update.queued_inputs {
join.controller join.controller
.queued_inputs .queued_inputs
.insert(input, (*join.time, attr)); .insert(input, (Time(0.0), attr));
join.controller.held_inputs.insert(input, attr); join.controller.held_inputs.insert(input, attr);
} }
for input in state_update.used_inputs { for input in state_update.used_inputs {
@ -283,7 +282,7 @@ impl Sys {
for input in state_update.removed_inputs { for input in state_update.removed_inputs {
join.controller.held_inputs.remove(&input); join.controller.held_inputs.remove(&input);
} }
join.controller.cull_queued_inputs(*join.time); join.controller.cull_queued_inputs(Time(0.0));
if state_update.swap_equipped_weapons { if state_update.swap_equipped_weapons {
output_events.emit_server(ServerEvent::InventoryManip( output_events.emit_server(ServerEvent::InventoryManip(
join.entity, join.entity,

View File

@ -191,6 +191,7 @@ impl<'a> System<'a> for Sys {
energy: read_data.energies.get(attacker), energy: read_data.energies.get(attacker),
combo: read_data.combos.get(attacker), combo: read_data.combos.get(attacker),
inventory: read_data.inventories.get(attacker), inventory: read_data.inventories.get(attacker),
stats: read_data.stats.get(attacker),
}); });
let target_info = TargetInfo { let target_info = TargetInfo {

View File

@ -298,6 +298,7 @@ fn dispatch_hit(
energy: read_data.energies.get(entity), energy: read_data.energies.get(entity),
combo: read_data.combos.get(entity), combo: read_data.combos.get(entity),
inventory: read_data.inventories.get(entity), inventory: read_data.inventories.get(entity),
stats: read_data.stats.get(entity),
}); });
let target_info = TargetInfo { let target_info = TargetInfo {

View File

@ -198,6 +198,7 @@ impl<'a> System<'a> for Sys {
energy: read_data.energies.get(entity), energy: read_data.energies.get(entity),
combo: read_data.combos.get(entity), combo: read_data.combos.get(entity),
inventory: read_data.inventories.get(entity), inventory: read_data.inventories.get(entity),
stats: read_data.stats.get(entity),
}); });
let target_info = TargetInfo { let target_info = TargetInfo {

View File

@ -3541,6 +3541,7 @@ fn cast_buff(kind: &str, data: BuffData, server: &mut Server, target: EcsEntity)
let ecs = &server.state.ecs(); let ecs = &server.state.ecs();
let mut buffs_all = ecs.write_storage::<comp::Buffs>(); let mut buffs_all = ecs.write_storage::<comp::Buffs>();
let stats = ecs.read_storage::<comp::Stats>(); let stats = ecs.read_storage::<comp::Stats>();
let healths = ecs.read_storage::<comp::Health>();
let time = ecs.read_resource::<Time>(); let time = ecs.read_resource::<Time>();
if let Some(mut buffs) = buffs_all.get_mut(target) { if let Some(mut buffs) = buffs_all.get_mut(target) {
buffs.insert( buffs.insert(
@ -3551,6 +3552,7 @@ fn cast_buff(kind: &str, data: BuffData, server: &mut Server, target: EcsEntity)
BuffSource::Command, BuffSource::Command,
*time, *time,
stats.get(target), stats.get(target),
healths.get(target),
), ),
*time, *time,
); );

View File

@ -865,18 +865,18 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
let uid_allocator = &ecs.read_resource::<UidAllocator>(); let uid_allocator = &ecs.read_resource::<UidAllocator>();
let players = &ecs.read_storage::<Player>(); let players = &ecs.read_storage::<Player>();
let buffs = &ecs.read_storage::<comp::Buffs>(); let buffs = &ecs.read_storage::<comp::Buffs>();
let stats = &ecs.read_storage::<comp::Stats>();
for ( for (
entity_b, entity_b,
pos_b, pos_b,
health_b, health_b,
(body_b_maybe, stats_b_maybe, ori_b_maybe, char_state_b_maybe, uid_b), (body_b_maybe, ori_b_maybe, char_state_b_maybe, uid_b),
) in ( ) in (
&ecs.entities(), &ecs.entities(),
&ecs.read_storage::<Pos>(), &ecs.read_storage::<Pos>(),
&ecs.read_storage::<Health>(), &ecs.read_storage::<Health>(),
( (
ecs.read_storage::<Body>().maybe(), ecs.read_storage::<Body>().maybe(),
ecs.read_storage::<Stats>().maybe(),
ecs.read_storage::<comp::Ori>().maybe(), ecs.read_storage::<comp::Ori>().maybe(),
ecs.read_storage::<CharacterState>().maybe(), ecs.read_storage::<CharacterState>().maybe(),
&ecs.read_storage::<Uid>(), &ecs.read_storage::<Uid>(),
@ -927,13 +927,14 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
energy: energies.get(entity), energy: energies.get(entity),
combo: combos.get(entity), combo: combos.get(entity),
inventory: inventories.get(entity), inventory: inventories.get(entity),
stats: stats.get(entity),
}); });
let target_info = combat::TargetInfo { let target_info = combat::TargetInfo {
entity: entity_b, entity: entity_b,
uid: *uid_b, uid: *uid_b,
inventory: inventories.get(entity_b), inventory: inventories.get(entity_b),
stats: stats_b_maybe, stats: stats.get(entity_b),
health: Some(health_b), health: Some(health_b),
pos: pos_b.0, pos: pos_b.0,
ori: ori_b_maybe, ori: ori_b_maybe,
@ -1159,15 +1160,16 @@ pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::Bu
} }
}, },
BuffChange::Refresh(kind) => { BuffChange::Refresh(kind) => {
let (buff_comp_kinds, buff_comp_buffs) = buffs.parts(); buffs
if let Some(buff_ids) = buff_comp_kinds.get(&kind) { .buffs
for id in buff_ids { .values_mut()
if let Some(buff) = buff_comp_buffs.get_mut(id) { .filter(|b| b.kind == kind)
// Resets buff timer to the original duration .for_each(|buff| {
buff.time = buff.data.duration; // Resets buff so that its remaining duration is equal to its original
} // duration
} buff.start_time = *time;
} buff.end_time = buff.data.duration.map(|dur| Time(time.0 + dur.0));
})
}, },
} }
} }
@ -1303,6 +1305,7 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option<
}; };
let time = ecs.read_resource::<Time>(); let time = ecs.read_resource::<Time>();
let stats = ecs.read_storage::<comp::Stats>(); let stats = ecs.read_storage::<comp::Stats>();
let healths = ecs.read_storage::<comp::Health>();
let buff = buff::Buff::new( let buff = buff::Buff::new(
BuffKind::Parried, BuffKind::Parried,
data, data,
@ -1310,6 +1313,7 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option<
source, source,
*time, *time,
stats.get(attacker), stats.get(attacker),
healths.get(attacker),
); );
server_eventbus.emit_now(ServerEvent::Buff { server_eventbus.emit_now(ServerEvent::Buff {
entity: attacker, entity: attacker,

View File

@ -224,6 +224,7 @@ impl StateExt for State {
Effect::Buff(buff) => { Effect::Buff(buff) => {
let time = self.ecs().read_resource::<Time>(); let time = self.ecs().read_resource::<Time>();
let stats = self.ecs().read_storage::<comp::Stats>(); let stats = self.ecs().read_storage::<comp::Stats>();
let healths = self.ecs().read_storage::<comp::Health>();
self.ecs() self.ecs()
.write_storage::<comp::Buffs>() .write_storage::<comp::Buffs>()
.get_mut(entity) .get_mut(entity)
@ -236,6 +237,7 @@ impl StateExt for State {
comp::BuffSource::Item, comp::BuffSource::Item,
*time, *time,
stats.get(entity), stats.get(entity),
healths.get(entity),
), ),
*time, *time,
) )

View File

@ -3,6 +3,7 @@ use super::{
CharacterSkeleton, SkeletonAttr, CharacterSkeleton, SkeletonAttr,
}; };
use common::states::utils::{AbilityInfo, StageSection}; use common::states::utils::{AbilityInfo, StageSection};
use core::f32::consts::PI;
pub struct DiveMeleeAnimation; pub struct DiveMeleeAnimation;
impl Animation for DiveMeleeAnimation { impl Animation for DiveMeleeAnimation {
@ -81,6 +82,54 @@ impl Animation for DiveMeleeAnimation {
next.control.orientation.rotate_x(move3 * -1.2); next.control.orientation.rotate_x(move3 * -1.2);
next.control.position += Vec3::new(0.0, move3 * 4.0, move3 * -8.0); next.control.position += Vec3::new(0.0, move3 * 4.0, move3 * -8.0);
}, },
Some("common.abilities.sword.heavy_pillar_thrust") => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time.powf(0.5), 0.0, 0.0),
Some(StageSection::Movement) => (anim_time.min(1.0).powf(0.5), 0.0, 0.0),
Some(StageSection::Action) => (1.0, anim_time.powi(2), 0.0),
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)),
_ => (0.0, 0.0, 0.0),
};
let move1alt1 = move1.min(0.5) * 2.0;
let move1alt2 = (move1.max(0.5) - 0.5) * 2.0;
let pullback = 1.0 - move3;
let move1 = move1 * pullback;
let move1alt1 = move1alt1 * pullback;
let move1alt2 = move1alt2 * pullback;
let move2 = move2 * pullback;
next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_l.orientation = Quaternion::rotation_x(s_a.shl.3 + move1alt2 * PI)
* Quaternion::rotation_y(s_a.shl.4);
next.hand_r.position = Vec3::new(
-s_a.sc.0 + 6.0 + move1alt1 * -12.0,
-4.0 + move1alt1 * 3.0,
-2.0,
);
next.hand_r.orientation =
Quaternion::rotation_x(0.9 + move1 * 0.5 + move1alt1 * PI);
next.control.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control.orientation = Quaternion::rotation_x(s_a.sc.3);
next.control.position += Vec3::new(
move1 * 6.0,
(1.0 - (move1 - 0.5).abs() * 2.0) * 3.0,
move1 * 22.0,
);
next.control.orientation.rotate_x(move1 * -1.5);
next.chest.orientation = Quaternion::rotation_x(move2 * -0.4);
next.head.orientation = Quaternion::rotation_x(move2 * 0.2);
next.belt.orientation = Quaternion::rotation_x(move2 * 0.4);
next.shorts.orientation = Quaternion::rotation_x(move2 * 0.8);
next.control.orientation.rotate_x(move2 * -0.4);
next.control.position += Vec3::new(0.0, 0.0, move2 * -10.0);
next.belt.position += Vec3::new(0.0, move2 * 2.0, move2 * 0.0);
next.shorts.position += Vec3::new(0.0, move2 * 4.0, move2 * 1.0);
next.chest.position += Vec3::new(0.0, move2 * -2.5, 0.0);
},
_ => {}, _ => {},
} }

View File

@ -109,53 +109,6 @@ impl Animation for FinisherMeleeAnimation {
next.control.position += Vec3::new(0.0, 0.0, move3 * 4.0); next.control.position += Vec3::new(0.0, 0.0, move3 * 4.0);
next.control.orientation.rotate_x(move3 * 0.6); next.control.orientation.rotate_x(move3 * 0.6);
}, },
Some("common.abilities.sword.crippling_finisher") => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time.powf(0.5), 0.0, 0.0),
Some(StageSection::Action) => (1.0, anim_time.powi(2), 0.0),
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)),
_ => (0.0, 0.0, 0.0),
};
let move1alt1 = move1.min(0.5) * 2.0;
let move1alt2 = (move1.max(0.5) - 0.5) * 2.0;
let pullback = 1.0 - move3;
let move1 = move1 * pullback;
let move1alt1 = move1alt1 * pullback;
let move1alt2 = move1alt2 * pullback;
let move2 = move2 * pullback;
next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_l.orientation = Quaternion::rotation_x(s_a.shl.3 + move1alt2 * PI)
* Quaternion::rotation_y(s_a.shl.4);
next.hand_r.position = Vec3::new(
-s_a.sc.0 + 6.0 + move1alt1 * -12.0,
-4.0 + move1alt1 * 3.0,
-2.0,
);
next.hand_r.orientation =
Quaternion::rotation_x(0.9 + move1 * 0.5 + move1alt1 * PI);
next.control.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control.orientation = Quaternion::rotation_x(s_a.sc.3);
next.control.position += Vec3::new(
move1 * 6.0,
(1.0 - (move1 - 0.5).abs() * 2.0) * 3.0,
move1 * 22.0,
);
next.control.orientation.rotate_x(move1 * -1.5);
next.chest.orientation = Quaternion::rotation_x(move2 * -0.4);
next.head.orientation = Quaternion::rotation_x(move2 * 0.2);
next.belt.orientation = Quaternion::rotation_x(move2 * 0.4);
next.shorts.orientation = Quaternion::rotation_x(move2 * 0.8);
next.control.orientation.rotate_x(move2 * -0.4);
next.control.position += Vec3::new(0.0, 0.0, move2 * -10.0);
next.belt.position += Vec3::new(0.0, move2 * 2.0, move2 * 0.0);
next.shorts.position += Vec3::new(0.0, move2 * 4.0, move2 * 1.0);
next.chest.position += Vec3::new(0.0, move2 * -2.5, 0.0);
},
Some("common.abilities.sword.cleaving_finisher") => { Some("common.abilities.sword.cleaving_finisher") => {
let (move1, move2, move3) = match stage_section { let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time.powf(0.5), 0.0, 0.0), Some(StageSection::Buildup) => (anim_time.powf(0.5), 0.0, 0.0),

View File

@ -29,6 +29,40 @@ impl Animation for SelfBuffAnimation {
next.second.orientation = Quaternion::rotation_z(0.0); next.second.orientation = Quaternion::rotation_z(0.0);
match ability_id { match ability_id {
Some("common.abilities.sword.heavy_fortitude") => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Movement) => (anim_time.powf(0.25), 0.0, 0.0),
Some(StageSection::Action) => (1.0, anim_time.powi(2), 0.0),
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)),
_ => (0.0, 0.0, 0.0),
};
let pullback = 1.0 - move3;
let move1 = move1 * pullback;
let move2 = move2 * pullback;
next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.shl.3) * Quaternion::rotation_y(s_a.shl.4);
next.hand_r.position =
Vec3::new(-s_a.sc.0 + 6.0 + move1 * -12.0, -4.0 + move1 * 3.0, -2.0);
next.hand_r.orientation = Quaternion::rotation_x(0.9 + move1 * 0.5);
next.control.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control.orientation = Quaternion::rotation_x(s_a.sc.3);
next.foot_l.position += Vec3::new(move1 * 1.0, move1 * 2.0, 0.0);
next.chest.orientation = Quaternion::rotation_z(move1 * -0.4);
next.head.orientation = Quaternion::rotation_z(move1 * 0.2);
next.shorts.orientation = Quaternion::rotation_z(move1 * 0.3);
next.belt.orientation = Quaternion::rotation_z(move1 * 0.1);
next.control.orientation.rotate_x(move1 * 0.4 + move2 * 0.6);
next.control.orientation.rotate_z(move1 * 0.4);
next.foot_r.position += Vec3::new(move2 * -1.0, move2 * -2.0, 0.0);
next.control.position += Vec3::new(move2 * 5.0, move2 * 7.0, move2 * 5.0);
next.chest.position += Vec3::new(0.0, 0.0, move2 * -1.0);
next.shorts.orientation.rotate_x(move2 * 0.2);
next.shorts.position += Vec3::new(0.0, move2 * 1.0, 0.0);
},
Some("common.abilities.sword.defensive_bulwark") => { Some("common.abilities.sword.defensive_bulwark") => {
let (move1, move2, move3) = match stage_section { let (move1, move2, move3) = match stage_section {
Some(StageSection::Movement) => (anim_time.powf(0.25), 0.0, 0.0), Some(StageSection::Movement) => (anim_time.powf(0.25), 0.0, 0.0),
@ -67,40 +101,6 @@ impl Animation for SelfBuffAnimation {
next.shorts.orientation.rotate_x(move2 * 0.2); next.shorts.orientation.rotate_x(move2 * 0.2);
next.control.orientation.rotate_z(move2 * 0.4); next.control.orientation.rotate_z(move2 * 0.4);
}, },
Some("common.abilities.sword.heavy_fortitude") => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Movement) => (anim_time.powf(0.25), 0.0, 0.0),
Some(StageSection::Action) => (1.0, anim_time.powi(2), 0.0),
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)),
_ => (0.0, 0.0, 0.0),
};
let pullback = 1.0 - move3;
let move1 = move1 * pullback;
let move2 = move2 * pullback;
next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.shl.3) * Quaternion::rotation_y(s_a.shl.4);
next.hand_r.position =
Vec3::new(-s_a.sc.0 + 6.0 + move1 * -12.0, -4.0 + move1 * 3.0, -2.0);
next.hand_r.orientation = Quaternion::rotation_x(0.9 + move1 * 0.5);
next.control.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control.orientation = Quaternion::rotation_x(s_a.sc.3);
next.foot_l.position += Vec3::new(move1 * 1.0, move1 * 2.0, 0.0);
next.chest.orientation = Quaternion::rotation_z(move1 * -0.4);
next.head.orientation = Quaternion::rotation_z(move1 * 0.2);
next.shorts.orientation = Quaternion::rotation_z(move1 * 0.3);
next.belt.orientation = Quaternion::rotation_z(move1 * 0.1);
next.control.orientation.rotate_x(move1 * 0.4 + move2 * 0.6);
next.control.orientation.rotate_z(move1 * 0.4);
next.foot_r.position += Vec3::new(move2 * -1.0, move2 * -2.0, 0.0);
next.control.position += Vec3::new(move2 * 5.0, move2 * 7.0, move2 * 5.0);
next.chest.position += Vec3::new(0.0, 0.0, move2 * -1.0);
next.shorts.orientation.rotate_x(move2 * 0.2);
next.shorts.position += Vec3::new(0.0, move2 * 1.0, 0.0);
},
Some("common.abilities.sword.mobility_agility") => { Some("common.abilities.sword.mobility_agility") => {
let (move1, move2, move3) = match stage_section { let (move1, move2, move3) = match stage_section {
Some(StageSection::Movement) => (anim_time.powf(0.25), 0.0, 0.0), Some(StageSection::Movement) => (anim_time.powf(0.25), 0.0, 0.0),

View File

@ -250,7 +250,7 @@ pub trait Animation {
let update_fn: common_dynlib::Symbol< let update_fn: common_dynlib::Symbol<
fn( fn(
&Self::Skeleton, &Self::Skeleton,
Self::Dependency<'a>, Self::Dependency<'_>,
f32, f32,
&mut f32, &mut f32,
&<Self::Skeleton as Skeleton>::Attr, &<Self::Skeleton as Skeleton>::Attr,