From c4226171db2f64b93cdb1b90d34d1df699963bb9 Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Wed, 19 Aug 2026 20:21:29 -0400 Subject: [PATCH] feat(unit-rules): helm wear --battle Prints what each hit took with it, which is the part that makes a worn machine that machine rather than a generic one. --- README.md | 6 +++++ crates/helm-cli/src/main.rs | 53 ++++++++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 001e016..a024335 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,12 @@ into a scenario's budget - damage a design until it is worth a given figure: helm wear "Atlas AS7-D" --to 1500 --megamek --bridge-dir +`--battle` does it with damage that has a shape - MegaMek's hit table, fire +that clusters, equipment lost when it gets through the plate - so every seed is +a different machine worth the same, and none of them is a mission kill: + + helm wear "Atlas AS7-D" --to 1500 --battle --seed 3 ... + `--clusters` is the one that matters. A ranked list of the worst disagreements shows whichever design is strangest; grouping them by what they have in common shows which *rule* is missing, and its second section ranks the failures by how diff --git a/crates/helm-cli/src/main.rs b/crates/helm-cli/src/main.rs index edabf4f..6a8af1e 100644 --- a/crates/helm-cli/src/main.rs +++ b/crates/helm-cli/src/main.rs @@ -95,6 +95,12 @@ WEAR machine that has already been in a fight into a scenario's budget. Plate first, then the frame under it, spread evenly across the locations. --to The figure to aim for. Required. + --battle Damage it in a battle instead of shaving plate evenly: + fire lands where the hit table sends it, clusters, and + takes equipment out when it gets through. Every seed is + a different machine worth the same, and none of them is + a mission kill. + --seed One battle in particular. Implies --battle. REPAIRS What a force needs putting right, location by location: points of plate, @@ -123,6 +129,8 @@ struct Opts { bench: bool, why: bool, to: Option, + battle: bool, + seed: Option, } fn parse_opts(args: &[String]) -> Result { @@ -138,6 +146,8 @@ fn parse_opts(args: &[String]) -> Result { bench: false, why: false, to: None, + battle: false, + seed: None, }; let mut i = 0; while i < args.len() { @@ -188,6 +198,19 @@ fn parse_opts(args: &[String]) -> Result { ); i += 2; } + "--battle" => { + o.battle = true; + i += 1; + } + "--seed" => { + o.seed = Some( + need(i)? + .parse() + .map_err(|_| "--seed takes a number".to_string())?, + ); + o.battle = true; + i += 2; + } "--why" => { o.why = true; i += 1; @@ -638,10 +661,27 @@ fn wear(args: &[String]) -> Result<(), String> { .find(|u| u.display_name().eq_ignore_ascii_case(name)) .ok_or_else(|| format!("no design called {name}"))?; - let worn = helm_bv::wear_to(unit, &catalogue, target).map_err(|e| e.to_string())?; + let worn = if opts.battle { + let battle = helm_bv::Battle { + seed: opts.seed.unwrap_or(1), + ..helm_bv::Battle::default() + }; + // A handful of battles rather than one: damage is drawn rather than + // solved, so the first one can overshoot where the third lands on the + // figure. A named seed asks for one battle in particular. + let tries = if opts.seed.is_some() { 1 } else { 6 }; + helm_bv::wear_in_battles(unit, &catalogue, target, &battle, tries) + } else { + helm_bv::wear_to(unit, &catalogue, target) + } + .map_err(|e| e.to_string())?; println!( - "{} is {} whole, {} worn{}", + "{}{} is {} whole, {} worn{}", unit.display_name(), + match worn.seed { + Some(seed) => format!(" (battle {seed})"), + None => String::new(), + }, worn.whole, worn.value, if worn.is_exact() { @@ -663,11 +703,16 @@ fn wear(args: &[String]) -> Result<(), String> { parts.push(format!("{} structure", repair.structure)); } println!(" {:<16} {}", repair.location, parts.join(", ")); + // What a penetrating hit took with it, which is the part of a worn + // machine that makes it that machine rather than a generic one. + for slot in &repair.destroyed { + println!(" {:<16} slot {}: {}", "", slot.at + 1, slot.what); + } } let total = helm_bv::Total::of(&repairs); println!( - " {:<16} {} armour and {} structure gone", - "", total.armor, total.structure + " {:<16} {} armour, {} structure and {} items gone", + "", total.armor, total.structure, total.items ); Ok(()) } -- 2.51.2