Heavy abilities

This commit is contained in:
Sam 2022-03-05 15:51:41 -05:00
parent 8a578bf3f6
commit 500e0ae3d0
21 changed files with 243 additions and 138 deletions

View File

@ -1,25 +1,47 @@
// TODO: Make actual ability, just for testing right now
BasicMelee(
energy_cost: 50,
buildup_duration: 0.3,
swing_duration: 0.1,
recover_duration: 0.2,
melee_constructor: (
kind: Stab(
damage: 10,
poise: 0,
knockback: 0,
energy_regen: 0,
ComboMelee2(
strikes: [
(
melee_constructor: (
kind: Slash(
damage: 7,
poise: 0,
knockback: 0,
energy_regen: 5,
),
range: 3.0,
angle: 45.0,
),
buildup_duration: 0.4,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.7,
ori_modifier: 0.6,
),
range: 5.0,
angle: 10.0,
),
ori_modifier: 1.0,
(
melee_constructor: (
kind: Slash(
damage: 10,
poise: 0,
knockback: 0,
energy_regen: 10,
),
range: 3.0,
angle: 45.0,
),
buildup_duration: 0.5,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.6,
ori_modifier: 0.6,
),
],
is_stance: true,
energy_cost_per_strike: 5,
meta: (
kind: Some(Sword(Balanced)),
kind: Some(Sword(Heavy)),
capabilities: (
// Block
bits: 0b00000010,
// Poise resistant during attack
bits: 0b00001000,
),
),
)
)

View File

@ -1,25 +1,30 @@
// TODO: Make actual ability, just for testing right now
BasicMelee(
energy_cost: 50,
buildup_duration: 0.3,
FinisherMelee(
energy_cost: 40,
buildup_duration: 0.4,
swing_duration: 0.1,
recover_duration: 0.2,
recover_duration: 0.4,
melee_constructor: (
kind: Stab(
damage: 10,
poise: 0,
kind: Slash(
damage: 20,
poise: 30,
knockback: 0,
energy_regen: 10,
),
scaled: Some(Slash(
damage: 0,
poise: 20,
knockback: 0,
energy_regen: 0,
),
range: 5.0,
angle: 10.0,
)),
range: 3.0,
angle: 15.0,
),
ori_modifier: 1.0,
scaling: Some((
target: Attack,
kind: Linear,
)),
minimum_combo: 10,
meta: (
kind: Some(Sword(Balanced)),
capabilities: (
// Block
bits: 0b00000010,
),
kind: Some(Sword(Heavy)),
),
)
)

View File

@ -1,25 +1,12 @@
// TODO: Make actual ability, just for testing right now
BasicMelee(
energy_cost: 50,
buildup_duration: 0.3,
swing_duration: 0.1,
recover_duration: 0.2,
melee_constructor: (
kind: Stab(
damage: 10,
poise: 0,
knockback: 0,
energy_regen: 0,
),
range: 5.0,
angle: 10.0,
),
ori_modifier: 1.0,
SelfBuff(
buildup_duration: 0.2,
cast_duration: 0.2,
recover_duration: 0.6,
buff_kind: Fortitude,
buff_strength: 1.0,
buff_duration: Some(5.0),
energy_cost: 40,
meta: (
kind: Some(Sword(Balanced)),
capabilities: (
// Block
bits: 0b00000010,
),
kind: Some(Sword(Heavy)),
),
)
)

View File

@ -1,25 +1,26 @@
// TODO: Make actual ability, just for testing right now
BasicMelee(
energy_cost: 50,
buildup_duration: 0.3,
swing_duration: 0.1,
recover_duration: 0.2,
melee_constructor: (
kind: Stab(
damage: 10,
poise: 0,
knockback: 0,
energy_regen: 0,
ComboMelee2(
strikes: [
(
melee_constructor: (
kind: Slash(
damage: 10,
poise: 20,
knockback: 0,
energy_regen: 10,
),
range: 4.0,
angle: 5.0,
),
buildup_duration: 0.2,
swing_duration: 0.1,
hit_timing: 0.6,
recover_duration: 0.4,
ori_modifier: 0.6,
),
range: 5.0,
angle: 10.0,
),
ori_modifier: 1.0,
],
is_stance: false,
energy_cost_per_strike: 10,
meta: (
kind: Some(Sword(Balanced)),
capabilities: (
// Block
bits: 0b00000010,
),
kind: Some(Sword(Heavy)),
),
)
)

View File

@ -61,6 +61,9 @@ buff-desc-wet = The ground rejects your feet, making it hard to stop.
## Ensnared
buff-title-ensnared = Ensnared
buff-desc-ensnared = Vines grasp at your legs, impeding your movement.
## Fortitude
buff-title-fortitude = Fortitude
buff-desc-fortitude = You can withstand staggers.
## Util
buff-text-over_seconds = over { $dur_secs } seconds
buff-text-for_seconds = for { $dur_secs } seconds

View File

@ -153,6 +153,7 @@ lazy_static! {
BuffKind::Ensnared => "ensnared",
BuffKind::Poisoned => "poisoned",
BuffKind::Hastened => "hastened",
BuffKind::Fortitude => "fortitude",
};
let mut buff_parser = HashMap::new();
for kind in BuffKind::iter() {

View File

@ -286,7 +286,13 @@ impl Attack {
let reduced_damage =
applied_damage * damage_reduction / (1.0 - damage_reduction);
let poise = reduced_damage * CRUSHING_POISE_FRACTION;
let change = -Poise::apply_poise_reduction(poise, target.inventory, msm);
let change = -Poise::apply_poise_reduction(
poise,
target.inventory,
msm,
target.char_state,
target.stats,
);
let poise_change = PoiseChange {
amount: change,
impulse: *dir,
@ -377,8 +383,13 @@ impl Attack {
}
},
CombatEffect::Poise(p) => {
let change = -Poise::apply_poise_reduction(*p, target.inventory, msm)
* strength_modifier;
let change = -Poise::apply_poise_reduction(
*p,
target.inventory,
msm,
target.char_state,
target.stats,
) * strength_modifier;
if change.abs() > Poise::POISE_EPSILON {
let poise_change = PoiseChange {
amount: change,
@ -536,8 +547,13 @@ impl Attack {
}
},
CombatEffect::Poise(p) => {
let change = -Poise::apply_poise_reduction(p, target.inventory, msm)
* strength_modifier;
let change = -Poise::apply_poise_reduction(
p,
target.inventory,
msm,
target.char_state,
target.stats,
) * strength_modifier;
if change.abs() > Poise::POISE_EPSILON {
let poise_change = PoiseChange {
amount: change,
@ -1146,7 +1162,8 @@ pub fn combat_rating(
// Normalized with a standard max poise of 100
let poise_rating = poise.base_max() as f32
/ 100.0
/ (1.0 - Poise::compute_poise_damage_reduction(inventory, msm)).max(0.00001);
/ (1.0 - Poise::compute_poise_damage_reduction(Some(inventory), msm, None, None))
.max(0.00001);
// Normalized with a standard crit multiplier of 1.2
let crit_rating = compute_crit_mult(Some(inventory), msm) / 1.2;

View File

@ -2544,5 +2544,6 @@ bitflags::bitflags! {
const ROLL_INTERRUPT = 0b00000001;
const BLOCK_INTERRUPT = 0b00000010;
const BUILDUP_PARRIES = 0b00000100;
const POISE_RESISTANT = 0b00001000;
}
}

View File

@ -51,6 +51,11 @@ pub enum BuffKind {
/// Strength scales strength of both effects linearly. 0.5 is a 50%
/// increase, 1.0 is a 100% increase.
Hastened,
// TODO: Consider non linear scaling?
/// Increases resistance to incomin poise over time
/// Strength scales the resistance linearly, values over 1 will usually do
/// nothing. 0.5 is 50%, 1.0 is 100%.
Fortitude,
// Debuffs
/// Does damage to a creature over time
/// Strength should be the DPS of the debuff
@ -99,7 +104,8 @@ impl BuffKind {
| BuffKind::IncreaseMaxHealth
| BuffKind::Invulnerability
| BuffKind::ProtectingWard
| BuffKind::Hastened => true,
| BuffKind::Hastened
| BuffKind::Fortitude => true,
BuffKind::Bleeding
| BuffKind::Cursed
| BuffKind::Burning
@ -181,6 +187,8 @@ pub enum BuffEffect {
AttackSpeed(f32),
/// Modifies ground friction of target
GroundFriction(f32),
/// Reduces poise damage taken after armor is accounted for by this fraction
PoiseReduction(f32),
}
/// Actual de/buff.
@ -378,6 +386,10 @@ impl Buff {
],
data.duration,
),
BuffKind::Fortitude => (
vec![BuffEffect::PoiseReduction(data.strength)],
data.duration,
),
};
Buff {
kind,

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
ability::Capability, inventory::item::armor::Friction, item::ConsumableKind, ControlAction, Density, Energy, InputAttr,
InputKind, Ori, Pos, Vel,
ability::Capability, inventory::item::armor::Friction, item::ConsumableKind, ControlAction,
Density, Energy, InputAttr, InputKind, Ori, Pos, Vel,
},
event::{LocalEvent, ServerEvent},
states::{

View File

@ -2,8 +2,9 @@ use crate::{
combat::{DamageContributor, DamageSource},
comp::{
self,
ability::Capability,
inventory::item::{armor::Protection, ItemKind, MaterialStatManifest},
CharacterState, Inventory,
CharacterState, Inventory, Stats,
},
resources::Time,
states,
@ -234,39 +235,58 @@ impl Poise {
/// Returns the total poise damage reduction provided by all equipped items
pub fn compute_poise_damage_reduction(
inventory: &Inventory,
inventory: Option<&Inventory>,
msm: &MaterialStatManifest,
char_state: Option<&CharacterState>,
stats: Option<&Stats>,
) -> f32 {
let protection = inventory
.equipped_items()
.filter_map(|item| {
if let ItemKind::Armor(armor) = &*item.kind() {
armor.stats(msm).poise_resilience
} else {
None
}
})
.map(|protection| match protection {
Protection::Normal(protection) => Some(protection),
Protection::Invincible => None,
})
.sum::<Option<f32>>();
match protection {
let protection = inventory.map_or(Some(0.0), |inv| {
inv.equipped_items()
.filter_map(|item| {
if let ItemKind::Armor(armor) = &*item.kind() {
armor.stats(msm).poise_resilience
} else {
None
}
})
.map(|protection| match protection {
Protection::Normal(protection) => Some(protection),
Protection::Invincible => None,
})
.sum::<Option<f32>>()
});
let from_inventory = match protection {
Some(dr) => dr / (60.0 + dr.abs()),
None => 1.0,
}
};
let from_char = {
let resistant = char_state
.and_then(|cs| cs.ability_info())
.map_or(false, |a| {
a.ability_meta
.capabilities
.contains(Capability::POISE_RESISTANT)
});
if resistant { 0.5 } else { 0.0 }
};
let from_stats = if let Some(stats) = stats {
stats.poise_reduction
} else {
0.0
};
1.0 - (1.0 - from_inventory) * (1.0 - from_char) * (1.0 - from_stats)
}
/// Modifies a poise change when optionally given an inventory to aid in
/// calculation of poise damage reduction
/// Modifies a poise change when optionally given an inventory and character
/// state to aid in calculation of poise damage reduction
pub fn apply_poise_reduction(
value: f32,
inventory: Option<&Inventory>,
msm: &MaterialStatManifest,
char_state: Option<&CharacterState>,
stats: Option<&Stats>,
) -> f32 {
inventory.map_or(value, |inv| {
value * (1.0 - Poise::compute_poise_damage_reduction(inv, msm))
})
value * (1.0 - Poise::compute_poise_damage_reduction(inventory, msm, char_state, stats))
}
}

View File

@ -49,6 +49,7 @@ impl Error for StatChangeError {}
pub struct Stats {
pub name: String,
pub damage_reduction: f32,
pub poise_reduction: f32,
pub max_health_modifiers: StatsModifier,
pub move_speed_modifier: f32,
pub attack_speed_modifier: f32,
@ -61,6 +62,7 @@ impl Stats {
Self {
name,
damage_reduction: 0.0,
poise_reduction: 0.0,
max_health_modifiers: StatsModifier::default(),
move_speed_modifier: 1.0,
attack_speed_modifier: 1.0,
@ -76,6 +78,7 @@ impl Stats {
/// Resets temporary modifiers to default values
pub fn reset_temp_modifiers(&mut self) {
self.damage_reduction = 0.0;
self.poise_reduction = 0.0;
self.max_health_modifiers = StatsModifier::default();
self.move_speed_modifier = 1.0;
self.attack_speed_modifier = 1.0;

View File

@ -414,10 +414,8 @@ pub fn handle_forced_movement(
// FRIC_GROUND temporarily used to normalize things around expected values
data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND
}) {
update.vel.0 += Vec2::broadcast(data.dt.0)
* accel
* Vec2::from(update.ori)
* strength;
update.vel.0 +=
Vec2::broadcast(data.dt.0) * accel * Vec2::from(update.ori) * strength;
}
},
ForcedMovement::Reverse(strength) => {

View File

@ -466,6 +466,9 @@ fn execute_effect(
BuffEffect::GroundFriction(gf) => {
stat.friction_modifier *= *gf;
},
BuffEffect::PoiseReduction(pr) => {
stat.poise_reduction = stat.poise_reduction.max(*pr).min(1.0);
},
};
}

View File

@ -552,6 +552,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
let falldmg = impact_energy / 1000.0;
let inventories = ecs.read_storage::<Inventory>();
let char_states = ecs.read_storage::<CharacterState>();
let stats = ecs.read_storage::<Stats>();
let time = ecs.read_resource::<Time>();
let msm = ecs.read_resource::<MaterialStatManifest>();
@ -583,8 +584,13 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
// Emit poise change
let poise_damage = -(mass.0 * vel.magnitude_squared() / 1500.0);
let poise_change =
Poise::apply_poise_reduction(poise_damage, inventories.get(entity), &msm);
let poise_change = Poise::apply_poise_reduction(
poise_damage,
inventories.get(entity),
&msm,
char_states.get(entity),
stats.get(entity),
);
let poise_change = comp::PoiseChange {
amount: poise_change,
impulse: Vec3::unit_z(),

View File

@ -183,7 +183,16 @@ impl StateExt for State {
},
Effect::Poise(poise) => {
let inventories = self.ecs().read_storage::<Inventory>();
let change = Poise::apply_poise_reduction(poise, inventories.get(entity), &msm);
let char_states = self.ecs().read_storage::<comp::CharacterState>();
let stats = self.ecs().read_storage::<comp::Stats>();
let change = Poise::apply_poise_reduction(
poise,
inventories.get(entity),
&msm,
char_states.get(entity),
stats.get(entity),
);
// Check to make sure the entity is not already stunned
if let Some(character_state) = self
.ecs()

View File

@ -101,7 +101,8 @@ pub fn localize_chat_message(
| BuffKind::Invulnerability
| BuffKind::ProtectingWard
| BuffKind::Frenzied
| BuffKind::Hastened => {
| BuffKind::Hastened
| BuffKind::Fortitude => {
tracing::error!("Player was killed by a positive buff!");
"hud-outcome-mysterious"
},

View File

@ -956,7 +956,13 @@ impl<'a> Widget for Bag<'a> {
let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
let stun_res_txt = format!(
"{}",
(100.0 * Poise::compute_poise_damage_reduction(inventory, self.msm)) as i32
(100.0
* Poise::compute_poise_damage_reduction(
Some(inventory),
self.msm,
None,
None
)) as i32
);
let stealth_txt = format!(
"{:.1}%",

View File

@ -1190,8 +1190,12 @@ impl<'a> Widget for Diary<'a> {
}
},
"Stun-Resistance" => {
let stun_res =
Poise::compute_poise_damage_reduction(self.inventory, self.msm);
let stun_res = Poise::compute_poise_damage_reduction(
Some(self.inventory),
self.msm,
None,
None,
);
format!("{:.2}%", stun_res * 100.0)
},
"Crit-Power" => {

View File

@ -4608,17 +4608,19 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
BuffKind::IncreaseMaxHealth { .. } => imgs.buff_healthplus_0,
BuffKind::Invulnerability => imgs.buff_invincibility_0,
BuffKind::ProtectingWard => imgs.buff_dmg_red_0,
BuffKind::Frenzied { .. } => imgs.buff_frenzy_0,
BuffKind::Hastened { .. } => imgs.buff_haste_0,
BuffKind::Frenzied => imgs.buff_frenzy_0,
BuffKind::Hastened => imgs.buff_haste_0,
// TODO: Get unique icon
BuffKind::Fortitude => imgs.buff_dmg_red_0,
// Debuffs
BuffKind::Bleeding { .. } => imgs.debuff_bleed_0,
BuffKind::Cursed { .. } => imgs.debuff_skull_0,
BuffKind::Burning { .. } => imgs.debuff_burning_0,
BuffKind::Crippled { .. } => imgs.debuff_crippled_0,
BuffKind::Frozen { .. } => imgs.debuff_frozen_0,
BuffKind::Wet { .. } => imgs.debuff_wet_0,
BuffKind::Ensnared { .. } => imgs.debuff_ensnared_0,
BuffKind::Poisoned { .. } => imgs.debuff_poisoned_0,
BuffKind::Bleeding => imgs.debuff_bleed_0,
BuffKind::Cursed => imgs.debuff_skull_0,
BuffKind::Burning => imgs.debuff_burning_0,
BuffKind::Crippled => imgs.debuff_crippled_0,
BuffKind::Frozen => imgs.debuff_frozen_0,
BuffKind::Wet => imgs.debuff_wet_0,
BuffKind::Ensnared => imgs.debuff_ensnared_0,
BuffKind::Poisoned => imgs.debuff_poisoned_0,
}
}
@ -4640,6 +4642,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> Cow<s
BuffKind::ProtectingWard => localized_strings.get_msg("buff-title-protectingward"),
BuffKind::Frenzied => localized_strings.get_msg("buff-title-frenzied"),
BuffKind::Hastened => localized_strings.get_msg("buff-title-hastened"),
BuffKind::Fortitude => localized_strings.get_msg("buff-title-fortitude"),
// Debuffs
BuffKind::Bleeding { .. } => localized_strings.get_msg("buff-title-bleed"),
BuffKind::Cursed { .. } => localized_strings.get_msg("buff-title-cursed"),
@ -4674,6 +4677,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
BuffKind::ProtectingWard => localized_strings.get_msg("buff-desc-protectingward"),
BuffKind::Frenzied => localized_strings.get_msg("buff-desc-frenzied"),
BuffKind::Hastened => localized_strings.get_msg("buff-desc-hastened"),
BuffKind::Fortitude => localized_strings.get_msg("buff-desc-fortitude"),
// Debuffs
BuffKind::Bleeding { .. } => localized_strings.get_msg("buff-desc-bleed"),
BuffKind::Cursed { .. } => localized_strings.get_msg("buff-desc-cursed"),

View File

@ -183,7 +183,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
| BuffKind::Wet
| BuffKind::Ensnared
| BuffKind::Poisoned
| BuffKind::Hastened => Cow::Borrowed(""),
| BuffKind::Hastened
| BuffKind::Fortitude => Cow::Borrowed(""),
};
write!(&mut description, "{}", buff_desc).unwrap();
@ -213,7 +214,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
| BuffKind::Wet
| BuffKind::Ensnared
| BuffKind::Poisoned
| BuffKind::Hastened => Cow::Borrowed(""),
| BuffKind::Hastened
| BuffKind::Fortitude => Cow::Borrowed(""),
}
} else if let BuffKind::Saturation | BuffKind::Regeneration | BuffKind::EnergyRegen =
buff.kind