3.7 KiB
Revalidating a slow native mortar aim
The native run logs_2026-09-19_08-21-23/rereintroduced-hq.log records the initial ballistic solution at mission time 32.9s (event 44) and the native shooting node at 57.2s (events 53–54). The gun took approximately 24.3 seconds to reach that node; the successor's 10-second authorization lease therefore rejected an otherwise still-fresh stationary delivered observation before any shot. The report was observed around 17.8 seconds, so this failure was distinct from the unchanged 60-second observation expiry.
ERR_MortarFiring.c now uses one ballistic solver body for initial aim and later authorization. It still calls the original node's installed GetAmmoInitialSpeedCoefficients(ResourceName, notnull array<float>), GetVerticalAimLimits(notnull IEntity, out float, out float) and the native BallisticTable.GetAimHeightOfProjectileAltitudeFromSource. Native coefficient order, elevation checks, moving-intercept iterations, terrain, range and safety checks are preserved. Only initial aiming writes native aim/charge output ports; revalidation never changes those outputs or the physical barrel.
Each successful initial aim retains its commanded geometry and a weak reference to the native solver node. An expired 10-second lease can be renewed only after verifying that the same node still addresses the same gun/ammunition and that the gun, delivered-report object, order and shot match the original command. A new solve must use the same charge, remain within 0.05 m of the original gun origin, within 0.1 m of the original impact and aim target in three dimensions, and within 0.05 seconds of the original flight time. The original solution remains the comparison reference across every renewal; repeated small target movements cannot accumulate into permission to fire somewhere the barrel never aimed.
The report's observation time is never changed. Deleted/replaced nodes, superseded reports/orders/shots, expired observations, failed prediction, changed charge/geometry or safety rejection still withhold the shot and use the existing next-HQ-tick cancellation. Physical projectile confirmation and cancellation both clear the retained command/node binding. Player-operated and unrelated artillery retain their native behavior.
The existing firing authorization runs before the native shooting action and again immediately before physical shell transfer. A renewal is not evidence of a shot: only the existing non-null native projectile event counts ammunition fired. Finite shell supply, native loading/alignment, projectile creation and airspace reservation remain unchanged.
tests/test_mortar_aim_renewal.py executes the actual equivalence, report freshness, command matching and renewal control-flow bodies with synthetic API inputs. It covers repeated recomputation, unchanged report age, expiry/future time, lost/replaced node inputs, wrong report/order/shot, failed solver, charge/origin/height/flight changes, non-finite flight values and cumulative drift against the original command. The ballistic output is deliberately a fake test boundary; these checks do not demonstrate native aim or firing. Installed source and failing-run hashes are recorded in mortar-aim-renewal-sources.json.
Native follow-up must rerun the real stationary-report firing fixture: require AIM REVALIDATED, unchanged report source/observation age, and a real projectile event before expiry, with complete raw logs and no engine/script errors. A moving report or changed gun/charge must still be withheld, and later reports must continue to arrive by physical radio delivery. Full salvo completion is subject to report freshness and finite physical supply; the change does not guarantee eight rounds from one expired report.