diff --git a/crates/helm-bv/src/battle.rs b/crates/helm-bv/src/battle.rs index f8ce9d0..e2bca54 100644 --- a/crates/helm-bv/src/battle.rs +++ b/crates/helm-bv/src/battle.rs @@ -37,10 +37,19 @@ //! location is ever destroyed - the frame is chewed to one point and no //! further, which also means no limb is ever blown off - the head is left //! alone below its plate, the engine takes at most one hit of the three that -//! destroy it and the gyro one of the two, and the walk stops before the -//! offensive rating falls below half of what the design started with. That -//! last one is the real test: a Mek with its guns shot away is a wreck to be -//! recovered, not a unit somebody is being asked to field. +//! destroy it and the gyro one of the two, and a machine always keeps +//! something it can fire. +//! +//! Guns do come off, and should: shooting one off is often the shortest way +//! down to a figure, and a lance where one Mek lost its PPC and kept its +//! lasers is a better fight than four machines with evenly thinned plate. +//! What is refused is the end of that - a machine with nothing left to fire, +//! or with less than a quarter of what it could throw, which is a tow rather +//! than a unit. +//! +//! Joints are left alone for the opposite reason: a shoulder, a hip or an +//! actuator carries no battle value at all, so shooting one off would hand +//! somebody a machine much weaker than the figure they asked for. //! //! Magazines are never hit either. One taking a critical explodes, and without //! CASE that is the end of the machine - and it is the only hit that can raise @@ -63,8 +72,16 @@ pub struct Battle { /// Percent of hits that come from behind, which is where a torso's thin /// rear plate matters. pub from_behind: u64, - /// How much of its offensive rating a machine must keep. Below this it is - /// a recovery job rather than a unit somebody can field. + /// How much of what it could throw a machine must keep. + /// + /// Shooting a gun off is often the best way down to a figure, and a lance + /// where one Mek lost its PPC and still has its lasers is a more + /// interesting fight than four machines with evenly thinned plate. What + /// this stops is the other end: a machine with nothing left worth firing + /// is a tow, not a unit somebody was asked to field. + /// + /// Whatever this is set to, a design always keeps *something* that can + /// fire - see [`wear_in_battle`]. pub keep_offense: f64, } @@ -74,7 +91,7 @@ impl Default for Battle { seed: 1, focus: 35, from_behind: 20, - keep_offense: 0.5, + keep_offense: 0.25, } } } @@ -131,9 +148,8 @@ pub fn wear_in_battle( battle: &Battle, ) -> Result { let whole = crate::battle_value_in(unit, catalogue, &Condition::undamaged())?; - let fresh_offense = crate::terms_in(unit, catalogue, &Condition::undamaged())? - .offensive - .total; + let fresh = crate::terms_in(unit, catalogue, &Condition::undamaged())?.offensive; + let (fresh_offense, fresh_weapons) = (fresh.total, fresh.weapons); let mut best = Worn { condition: Condition::undamaged(), value: whole, @@ -165,10 +181,16 @@ pub fn wear_in_battle( continue; }; let terms = crate::terms_in(unit, catalogue, &condition)?; - // A machine that can no longer shoot is not one to field. The hit that - // took it there is undone, so the answer is always a machine that - // could still fight. - if terms.offensive.total < fresh_offense * battle.keep_offense { + // Guns come off - it is often the best way down to a figure, and it is + // what makes one machine in a lance interesting rather than four with + // evenly thinned plate. What is refused is the end of that: a machine + // with nothing left to fire, or with so little left that it is a tow + // rather than a unit. The hit that would do it is undone, so what + // comes back is always something somebody could take onto a board. + // An IndustrialMek that never carried a weapon has none to lose, and + // guarding one it does not have would make it undamageable. + let disarmed = fresh_weapons > 0.0 && terms.offensive.weapons <= 0.0; + if disarmed || terms.offensive.total < fresh_offense * battle.keep_offense { field.undo(); continue; } @@ -389,6 +411,13 @@ impl Field { if is_ammo(what) { continue; } + // An actuator or a hip is worth no battle value at all and is + // worth a great deal in play - a wrecked hip halves what a Mek can + // walk. Hitting one would hand somebody a machine much weaker than + // the figure they asked for, which is the opposite of the job. + if is_actuator(what) { + continue; + } candidates.push(at); } let Some(&at) = rng.choose(&candidates) else { @@ -475,6 +504,15 @@ fn is_gyro(what: &str) -> bool { what.contains("Gyro") } +/// A joint: shoulders, hips and the actuators between them. +/// +/// Legitimate targets in the game and useless ones here. None of them carries +/// any battle value, so shooting one off cripples a machine without moving the +/// figure it is being worn down to. +fn is_actuator(what: &str) -> bool { + what.contains("Actuator") || what.contains("Shoulder") || what.contains("Hip") +} + /// A magazine, which does not take a critical hit and leave a live Mek. fn is_ammo(what: &str) -> bool { what.contains("Ammo")