Cut 12.2 Stryker fix batch: one range, five real gaps
The first Stryker pass (375 mutants, 69 survivors on this cut's own changed lines) found one real split-authority bug and several untested edges: - HitProbability recomputed target.Position - source.Position a second time for PSpread's range, a second place the same subtraction could flip sign. PFire now returns its own gate-computed range by out parameter; HitProbability reads that one value instead of a second copy. - TravelDirection's point-blank fallback and Fire's null-target guard around it were only ever exercised at shooter/target pairs where the real direction happened to equal the (0,1) fallback by coincidence. - diagnostic.PFire (presentation) was only ever checked at PSensor == 1, where Accuracy * PSensor and Accuracy / PSensor agree. - CommitProbability's four-factor product was only ever checked with pDeviation == 1 (no fixture moved a target between fire and commit), where * and / by 1 agree. - ResolveAimPoint's zero-cells fallback (an aimed item that no longer occupies any cell) was never reached; every fixture's marker survives to be hit. New tests: TravelDirectionMatchesThePredictedInterceptAndFallsBackAtPointBlank, DiagnosticPFireIsAccuracyTimesPSensor, CommitProbabilityIsTheExactProductOfItsFourFactors, AimPointFallsBackWhenTheAimedItemNoLongerOccupiesAnyCell. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7dbb3bcf7a
commit
efe4f7c954
2 changed files with 123 additions and 6 deletions
|
|
@ -152,13 +152,20 @@ public static class FireControl
|
|||
// freezes (R10), gated exactly as HitProbability gates. Nothing about the target's facing or silhouette
|
||||
// enters here; that is Silhouette's and PSpread's job, priced fresh by HitProbability below and again,
|
||||
// live, by Commit at the commit tick. 0 the moment any gate closes.
|
||||
private static float PFire(Weapon weapon, Entity source, Entity target)
|
||||
private static float PFire(Weapon weapon, Entity source, Entity target) => PFire(weapon, source, target, out _);
|
||||
|
||||
// The `out range` overload is HitProbability's own gate call -- one computation of `target.Position -
|
||||
// source.Position`, not two. HitProbability used to recompute its own copy for PSpread, which is exactly
|
||||
// the kind of split authority Cut 3 named as the risk (two places computing the same vector, free to
|
||||
// drift or to have their subtraction order flipped in only one of them).
|
||||
private static float PFire(Weapon weapon, Entity source, Entity target, out float range)
|
||||
{
|
||||
range = 0f;
|
||||
if (target == null) return 0f;
|
||||
if (!source.VisibleEntities.Contains(target)) return 0f;
|
||||
|
||||
var toTarget = target.Position - source.Position;
|
||||
var range = length(toTarget);
|
||||
range = length(toTarget);
|
||||
if (range < weapon.MinRange || range > weapon.Range) return 0f;
|
||||
if (weapon is LockWeapon lockWeapon && !lockWeapon.IsLocked) return 0f;
|
||||
if (!InArc(weapon.Item, toTarget)) return 0f;
|
||||
|
|
@ -170,10 +177,9 @@ public static class FireControl
|
|||
|
||||
public static float HitProbability(Weapon weapon, Entity source, Entity target)
|
||||
{
|
||||
var pFire = PFire(weapon, source, target);
|
||||
var pFire = PFire(weapon, source, target, out var range);
|
||||
if (pFire <= 0f) return 0f;
|
||||
|
||||
var range = length(target.Position - source.Position);
|
||||
var targetHull = source.ItemManager.GetData(target.Hull) as HullData;
|
||||
var bearing = Bearing(target, TravelDirection(weapon, source, target));
|
||||
var sil = Silhouette(target, targetHull, source.ResolvedTargetItem, bearing, Precision(source));
|
||||
|
|
|
|||
Loading…
Reference in a new issue