feat: added puncture and penetrate
This commit is contained in:
parent
451efd2bb7
commit
6c3c50689a
4 changed files with 104 additions and 59 deletions
|
|
@ -62,6 +62,10 @@ pub enum WeaponBonusType {
|
||||||
// Attack nullification types
|
// Attack nullification types
|
||||||
Homerun,
|
Homerun,
|
||||||
Parry,
|
Parry,
|
||||||
|
|
||||||
|
// Armour mitigation
|
||||||
|
Puncture,
|
||||||
|
Penetrate,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
|
|
@ -183,6 +187,12 @@ impl BonusPartDamageBonus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Component)]
|
||||||
|
pub enum ArmourBypassBonus {
|
||||||
|
Puncture { chance: f32 },
|
||||||
|
Penetrate { mitigation: f32 },
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Component, Display)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Component, Display)]
|
||||||
#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "json", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))]
|
#[cfg_attr(feature = "json", serde(rename_all = "snake_case"))]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use proxisim_models::bundle::{
|
use proxisim_models::bundle::{
|
||||||
armour::{ArmourBodyPart, ArmourBodyParts},
|
armour::{ArmourBodyPart, ArmourBodyParts},
|
||||||
bonus::DamageMitigationBonus,
|
bonus::{ArmourBypassBonus, DamageMitigationBonus},
|
||||||
player::{
|
player::{
|
||||||
Attacker, BodyPart, ChooseWeapon, CombatTurns, Current, CurrentTarget, Defeated, Defender,
|
Attacker, BodyPart, ChooseWeapon, CombatTurns, Current, CurrentTarget, Defeated, Defender,
|
||||||
FightEndType, Health, PartDamageBonus, Player, PlayerStrategy, Weapons,
|
FightEndType, Health, PartDamageBonus, Player, PlayerStrategy, Weapons,
|
||||||
|
|
@ -188,10 +188,11 @@ pub fn use_damaging_weapon(
|
||||||
),
|
),
|
||||||
(With<CurrentTarget>, Without<Current>),
|
(With<CurrentTarget>, Without<Current>),
|
||||||
>,
|
>,
|
||||||
(armour_q, damage_q, mut mitigation_q): (
|
(armour_q, damage_q, mut mitigation_q, bypass_q): (
|
||||||
Query<&ArmourBodyPart>,
|
Query<&ArmourBodyPart>,
|
||||||
Query<(Entity, &DeferredDamage)>,
|
Query<(Entity, &DeferredDamage)>,
|
||||||
Query<Option<&mut DamageMitigationBonus>>,
|
Query<Option<&mut DamageMitigationBonus>>,
|
||||||
|
Query<(&ArmourBypassBonus)>,
|
||||||
),
|
),
|
||||||
(damage_proc_q, part_bonus_q): (Query<&DamageProcEffect>, Query<&PartDamageBonus>),
|
(damage_proc_q, part_bonus_q): (Query<&DamageProcEffect>, Query<&PartDamageBonus>),
|
||||||
(mut ammo_q, mut temp_q): (
|
(mut ammo_q, mut temp_q): (
|
||||||
|
|
@ -368,8 +369,8 @@ pub fn use_damaging_weapon(
|
||||||
metrics.increment_counter(Some(weapon), "hit", 1);
|
metrics.increment_counter(Some(weapon), "hit", 1);
|
||||||
|
|
||||||
let armour_parts = armour_q.get(armour_parts.0[body_part.into()]).unwrap();
|
let armour_parts = armour_q.get(armour_parts.0[body_part.into()]).unwrap();
|
||||||
let piece = rng.sample(armour_parts);
|
let mut piece = rng.sample(armour_parts);
|
||||||
let armour_mitigation = piece.map_or(0.0, |p| p.armour_value);
|
let mut armour_mitigation = piece.map_or(0.0, |p| p.armour_value);
|
||||||
|
|
||||||
// NOTE: Proxima's simulator seems to have the damage spread be between 95% and 105%,
|
// NOTE: Proxima's simulator seems to have the damage spread be between 95% and 105%,
|
||||||
// but from my brief tests it seems that 100% to 110% lines up better, at least for h2h.
|
// but from my brief tests it seems that 100% to 110% lines up better, at least for h2h.
|
||||||
|
|
@ -384,9 +385,25 @@ pub fn use_damaging_weapon(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bonus_mitigation = match piece {
|
let bonus_mitigation = 'block: {
|
||||||
Some(piece) => {
|
match piece {
|
||||||
if let Some(mut mitigation) = mitigation_q.get_mut(piece.armour).unwrap() {
|
Some(part) => {
|
||||||
|
for bypass in bypass_q.iter_many(children) {
|
||||||
|
match bypass {
|
||||||
|
ArmourBypassBonus::Penetrate { mitigation } => {
|
||||||
|
armour_mitigation *= 1.0 - mitigation;
|
||||||
|
}
|
||||||
|
ArmourBypassBonus::Puncture { chance } => {
|
||||||
|
if *chance >= 1.0 || rng.random_bool(*chance as f64) {
|
||||||
|
armour_mitigation = 0.0;
|
||||||
|
piece = None;
|
||||||
|
break 'block 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut mitigation) = mitigation_q.get_mut(part.armour).unwrap() {
|
||||||
match mitigation.as_mut() {
|
match mitigation.as_mut() {
|
||||||
DamageMitigationBonus::Impregnable { mitigation }
|
DamageMitigationBonus::Impregnable { mitigation }
|
||||||
if *slot == WeaponSlot::Melee =>
|
if *slot == WeaponSlot::Melee =>
|
||||||
|
|
@ -413,7 +430,7 @@ pub fn use_damaging_weapon(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DamageMitigationBonus::Kinetokinesis { mitigation } => {
|
DamageMitigationBonus::Kinetokinesis { mitigation } => {
|
||||||
commands.entity(piece.armour).insert(
|
commands.entity(part.armour).insert(
|
||||||
DamageMitigationBonus::ActiveKinetokinesis {
|
DamageMitigationBonus::ActiveKinetokinesis {
|
||||||
mitigation: *mitigation,
|
mitigation: *mitigation,
|
||||||
remaining_turns: 10,
|
remaining_turns: 10,
|
||||||
|
|
@ -428,7 +445,7 @@ pub fn use_damaging_weapon(
|
||||||
*remaining_turns -= 1;
|
*remaining_turns -= 1;
|
||||||
|
|
||||||
if *remaining_turns == 0 {
|
if *remaining_turns == 0 {
|
||||||
commands.entity(piece.armour).insert(
|
commands.entity(part.armour).insert(
|
||||||
DamageMitigationBonus::Kinetokinesis {
|
DamageMitigationBonus::Kinetokinesis {
|
||||||
mitigation: *mitigation,
|
mitigation: *mitigation,
|
||||||
},
|
},
|
||||||
|
|
@ -444,6 +461,7 @@ pub fn use_damaging_weapon(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => 0.0,
|
None => 0.0,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: special ammo
|
// TODO: special ammo
|
||||||
|
|
@ -503,7 +521,7 @@ pub fn use_damaging_weapon(
|
||||||
bonus.spawn(target, &mut effects, &mut rng.0);
|
bonus.spawn(target, &mut effects, &mut rng.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DamageProcEffect::DamageOverTimer { value, kind } => {
|
DamageProcEffect::DamageOverTime { value, kind } => {
|
||||||
let chance = (value / 100.0) as f64;
|
let chance = (value / 100.0) as f64;
|
||||||
if chance > 1.0 || rng.random_bool(chance) {
|
if chance > 1.0 || rng.random_bool(chance) {
|
||||||
match kind {
|
match kind {
|
||||||
|
|
@ -562,8 +580,8 @@ pub fn use_damaging_weapon(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Technically only douple tap and blindfire have this condition, but we can run into with
|
// Technically only douple tap and blindfire have this condition, but we can run into
|
||||||
// invalid bonus/weapon combinations without checking this for all bonuses
|
// panics with invalid bonus/weapon combinations without checking this for all bonuses
|
||||||
if ammo.as_ref().is_some_and(|(a, _, _)| a.0 == 0) {
|
if ammo.as_ref().is_some_and(|(a, _, _)| a.0 == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -679,5 +697,7 @@ pub(crate) fn configure(stages: &mut Stages) {
|
||||||
.before(change_roles),
|
.before(change_roles),
|
||||||
);
|
);
|
||||||
stages.post_fight.add_systems(record_post_fight_stats);
|
stages.post_fight.add_systems(record_post_fight_stats);
|
||||||
stages.restore.add_systems(restore_initial_state);
|
stages
|
||||||
|
.restore
|
||||||
|
.add_systems((restore_initial_state, restore_health));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use proxisim_models::bundle::{
|
use proxisim_models::bundle::{
|
||||||
bonus::{BonusPartDamageBonus, BonusValue, WeaponBonusType},
|
bonus::{ArmourBypassBonus, BonusPartDamageBonus, BonusValue, WeaponBonusType},
|
||||||
player::PartDamageBonus,
|
player::PartDamageBonus,
|
||||||
stat::{
|
stat::{
|
||||||
AdditiveBonus, AmmoControl, Clips, CritRate, DamageBonus, SimpleStatBonus,
|
AdditiveBonus, AmmoControl, Clips, CritRate, DamageBonus, SimpleStatBonus,
|
||||||
|
|
@ -431,12 +431,27 @@ pub(crate) fn prepare_bonuses(
|
||||||
WeaponBonusType::Bleed => {
|
WeaponBonusType::Bleed => {
|
||||||
commands
|
commands
|
||||||
.entity(weapon.parent())
|
.entity(weapon.parent())
|
||||||
.with_child(DamageProcEffect::DamageOverTimer {
|
.with_child(DamageProcEffect::DamageOverTime {
|
||||||
value: value.0,
|
value: value.0,
|
||||||
kind: DamageOverTimeType::Bleed,
|
kind: DamageOverTimeType::Bleed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WeaponBonusType::Puncture => {
|
||||||
|
commands
|
||||||
|
.entity(weapon.parent())
|
||||||
|
.with_child(ArmourBypassBonus::Puncture {
|
||||||
|
chance: value.0 / 100.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
WeaponBonusType::Penetrate => {
|
||||||
|
commands
|
||||||
|
.entity(weapon.parent())
|
||||||
|
.with_child(ArmourBypassBonus::Penetrate {
|
||||||
|
mitigation: value.0 / 100.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
val => unimplemented!("{val:?}"),
|
val => unimplemented!("{val:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ pub enum DamageProcEffect {
|
||||||
value: f32,
|
value: f32,
|
||||||
bonus: SelfStatusEffect,
|
bonus: SelfStatusEffect,
|
||||||
},
|
},
|
||||||
DamageOverTimer {
|
DamageOverTime {
|
||||||
value: f32,
|
value: f32,
|
||||||
kind: DamageOverTimeType,
|
kind: DamageOverTimeType,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue