feat: added deadly and execute

This commit is contained in:
TotallyNot 2025-11-04 17:29:46 +01:00
parent 1c66dbf499
commit 9550cd2bbb
Signed by: pyrite
GPG key ID: 7F1BA9170CD35D15
5 changed files with 237 additions and 47 deletions

View file

@ -66,6 +66,10 @@ pub enum WeaponBonusType {
// Armour mitigation // Armour mitigation
Puncture, Puncture,
Penetrate, Penetrate,
// Misc
Execute,
Deadly,
} }
#[derive(Component)] #[derive(Component)]

View file

@ -1,22 +1,24 @@
{ {
"level": 100, "level": 100,
"name": "Evil Pyrit", "name": "Lynaikitsu",
"stats": { "stats": {
"str": 1415286156, "str": 8910798,
"spd": 1344108854, "spd": 9802681,
"def": 1425974550, "def": 3132400125,
"dex": 2182078916 "dex": 787
}, },
"strategy": { "strategy": {
"type": "in_order", "type": "in_order",
"order": [], "order": [
"primary"
],
"reload": false "reload": false
}, },
"education": { "education": {
"bio2350": true, "bio2350": true,
"bio2380": true, "bio2380": true,
"bio2410": true, "bio2410": true,
"cbt2790": true, "cbt2790": false,
"cbt2820": true, "cbt2820": true,
"cbt2830": true, "cbt2830": true,
"cbt2840": true, "cbt2840": true,
@ -25,41 +27,41 @@
"cbt2125": true, "cbt2125": true,
"gen2116": true, "gen2116": true,
"gen2119": true, "gen2119": true,
"haf2104": true, "haf2104": false,
"haf2105": true, "haf2105": false,
"haf2106": true, "haf2106": false,
"haf2107": true, "haf2107": false,
"haf2108": true, "haf2108": false,
"haf2109": true, "haf2109": false,
"his2160": true, "his2160": true,
"his2170": true, "his2170": true,
"mth2240": true, "mth2240": false,
"mth2250": true, "mth2250": false,
"mth2260": true, "mth2260": false,
"mth2320": true, "mth2320": false,
"mth2310": true, "mth2310": true,
"mth3330": true, "mth3330": true,
"psy2640": true, "psy2640": false,
"psy2650": true, "psy2650": false,
"psy2660": true, "psy2660": false,
"psy2670": true, "psy2670": false,
"def2710": true, "def2710": false,
"def2730": true, "def2730": false,
"def2740": true, "def2740": false,
"def2750": true, "def2750": false,
"def2760": true, "def2760": false,
"def3770": true, "def3770": true,
"spt2480": true, "spt2480": true,
"spt2490": true, "spt2490": false,
"spt2500": true "spt2500": false
}, },
"merits": { "merits": {
"life": 10, "life": 10,
"crits": 10, "crits": 10,
"brawn": 10, "brawn": 0,
"protection": 10, "protection": 0,
"sharpness": 10, "sharpness": 0,
"evasion": 10, "evasion": 0,
"heavy_artillery_mastery": 0, "heavy_artillery_mastery": 0,
"machine_gun_mastery": 0, "machine_gun_mastery": 0,
"rifle_mastery": 5, "rifle_mastery": 5,
@ -85,13 +87,137 @@
"weapons": { "weapons": {
"primary": null, "primary": null,
"secondary": null, "secondary": null,
"melee": null "melee": null,
"temporary": null
}, },
"armour": { "armour": {
"helmet": null, "helmet": {
"body": null, "slot": "head",
"pants": null, "id": 680,
"gloves": null, "name": "EOD Helmet",
"boots": null "base_armour": 55,
"armour": 55,
"coverage": {
"body": 10.220000267028809,
"heart": 0,
"stomach": 0,
"chest": 0,
"arm": 0,
"groin": 0,
"leg": 0,
"throat": 42.900001525878906,
"hand": 0,
"foot": 0,
"head": 100
},
"immunities": [
"concussion_grenade",
"pepper_spray"
],
"bonus": {
"kind": "impassable",
"value": 0
}
},
"body": {
"slot": "body",
"id": 681,
"name": "EOD Apron",
"base_armour": 55,
"armour": 55,
"coverage": {
"body": 55.2400016784668,
"heart": 100,
"stomach": 100,
"chest": 100,
"arm": 100,
"groin": 100,
"leg": 17.579999923706055,
"throat": 100,
"hand": 16.8799991607666,
"foot": 0,
"head": 4.679999828338623
},
"immunities": [],
"bonus": {
"kind": "impassable",
"value": 0
}
},
"pants": {
"slot": "legs",
"id": 682,
"name": "EOD Pants",
"base_armour": 55,
"armour": 55,
"coverage": {
"body": 26.010000228881836,
"heart": 0,
"stomach": 23.940000534057617,
"chest": 0,
"arm": 0,
"groin": 100,
"leg": 100,
"throat": 0,
"hand": 0,
"foot": 20.1299991607666,
"head": 0
},
"immunities": [],
"bonus": {
"kind": "impassable",
"value": 0
}
},
"gloves": {
"slot": "hands",
"id": 684,
"name": "EOD Gloves",
"base_armour": 55,
"armour": 55,
"coverage": {
"body": 14.369999885559082,
"heart": 0,
"stomach": 0,
"chest": 0,
"arm": 0.5,
"groin": 0,
"leg": 0,
"throat": 0,
"hand": 100,
"foot": 0,
"head": 0
},
"immunities": [],
"bonus": {
"kind": "impassable",
"value": 0
}
},
"boots": {
"slot": "feet",
"id": 683,
"name": "EOD Boots",
"base_armour": 55,
"armour": 55,
"coverage": {
"body": 15.170000076293945,
"heart": 0,
"stomach": 0,
"chest": 0,
"arm": 0,
"groin": 0,
"leg": 6.190000057220459,
"throat": 0,
"hand": 0,
"foot": 100,
"head": 0
},
"immunities": [],
"bonus": {
"kind": "impassable",
"value": 0
}
}
} }
} }

View file

@ -192,7 +192,7 @@ pub fn use_damaging_weapon(
Query<&ArmourBodyPart>, Query<&ArmourBodyPart>,
Query<(Entity, &DeferredDamage)>, Query<(Entity, &DeferredDamage)>,
Query<Option<&mut DamageMitigationBonus>>, Query<Option<&mut DamageMitigationBonus>>,
Query<(&ArmourBypassBonus)>, 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): (
@ -245,6 +245,7 @@ pub fn use_damaging_weapon(
commands.entity(instance).despawn(); commands.entity(instance).despawn();
if p_health.0 == 0 { if p_health.0 == 0 {
commands.entity(player).insert(Defeated);
log!(logger, "fight_end", { log!(logger, "fight_end", {
actor: target, actor: target,
recipient: player, recipient: player,
@ -466,7 +467,7 @@ pub fn use_damaging_weapon(
// TODO: special ammo // TODO: special ammo
let dmg = dmg_intrinsic let mut dmg = dmg_intrinsic
* w_dmg.0 * w_dmg.0
* (1.0 + dmg_bonus.value) * (1.0 + dmg_bonus.value)
* (1.0 - armour_mitigation) * (1.0 - armour_mitigation)
@ -474,12 +475,12 @@ pub fn use_damaging_weapon(
* (1.0 - bonus_mitigation) * (1.0 - bonus_mitigation)
* mult * mult
* dmg_spread; * dmg_spread;
let dmg = dmg.round() as u32; let mut dmg_i = dmg.round() as u32;
metrics.record_histogram(Some(player), "dmg", dmg); metrics.record_histogram(Some(player), "dmg", dmg_i);
metrics.record_histogram(Some(weapon), "dmg", dmg); metrics.record_histogram(Some(weapon), "dmg", dmg_i);
if dmg > 0 { if dmg_i > 0 {
for effect in damage_proc_q.iter_many(children) { for effect in damage_proc_q.iter_many(children) {
match *effect { match *effect {
DamageProcEffect::MultiTurn { value, bonus } => { DamageProcEffect::MultiTurn { value, bonus } => {
@ -528,18 +529,54 @@ pub fn use_damaging_weapon(
DamageOverTimeType::Bleed => { DamageOverTimeType::Bleed => {
commands commands
.entity(target) .entity(target)
.insert(DamageOverTime::<Bleed>::new(dmg)); .insert(DamageOverTime::<Bleed>::new(dmg_i));
} }
} }
} }
} }
DamageProcEffect::Deadly { chance } => {
if chance >= 1.0 || rng.random_bool(chance as f64) {
dmg = dmg_intrinsic
* w_dmg.0
* (1.0 + dmg_bonus.value + 5.0)
* (1.0 - armour_mitigation)
* (1.0 - def_mitigation)
* (1.0 - bonus_mitigation)
* mult
* dmg_spread;
dmg_i = dmg.round() as u32;
}
}
DamageProcEffect::Execute { cutoff } => {
if health.0 as f32 / target_max_health.value as f32 <= cutoff {
commands.entity(target).insert(Defeated);
let health_before = health.0;
health.0 = 0;
log!(logger, "executed", {
actor: player,
recipient: target,
health_before,
});
log!(logger, "fight_end", {
actor: player,
recipient: target,
fight_end_type: %if attacker {
FightEndType::Victory
} else {
FightEndType::Loss
},
});
metrics.increment_counter(Some(player), "victory", 1);
return;
}
}
} }
} }
} }
let health_before = health.0; let health_before = health.0;
health.0 = health.0.saturating_sub(dmg as u16); health.0 = health.0.saturating_sub(dmg_i as u16);
log!(logger, "hit_target", { log!(logger, "hit_target", {
actor: player, actor: player,
@ -551,7 +588,7 @@ pub fn use_damaging_weapon(
rounds, rounds,
health_before: health_before, health_before: health_before,
health_after: health.0, health_after: health.0,
dmg, dmg: dmg_i,
dmg_spread, dmg_spread,
dmg_intrinsic, dmg_intrinsic,
dmg_weapon: w_dmg.0, dmg_weapon: w_dmg.0,

View file

@ -452,6 +452,21 @@ pub(crate) fn prepare_bonuses(
}); });
} }
WeaponBonusType::Deadly => {
commands
.entity(weapon.parent())
.with_child(DamageProcEffect::Deadly {
chance: value.0 / 100.0,
});
}
WeaponBonusType::Execute => {
commands
.entity(weapon.parent())
.with_child(DamageProcEffect::Execute {
cutoff: value.0 / 100.0,
});
}
val => unimplemented!("{val:?}"), val => unimplemented!("{val:?}"),
} }
} }

View file

@ -149,6 +149,7 @@ pub enum DamageProcEffect {
value: f32, value: f32,
bonus: OpponentStatusEffect, bonus: OpponentStatusEffect,
}, },
SelfEffect { SelfEffect {
value: f32, value: f32,
bonus: SelfStatusEffect, bonus: SelfStatusEffect,
@ -157,6 +158,13 @@ pub enum DamageProcEffect {
value: f32, value: f32,
kind: DamageOverTimeType, kind: DamageOverTimeType,
}, },
Execute {
cutoff: f32,
},
Deadly {
chance: f32,
},
} }
fn apply_passives( fn apply_passives(