// Decisions consume detached HQ memory and delivered friendly check-ins only. class ERR_AirSupportSituation { int BeliefId; int Samples; int Spotted; int GroundFit; int CommittedFit; int ConfirmedLosses; float Age; float Confidence; float Radius; float Speed; bool MotionKnown; bool Infantry; bool AircraftReady; bool ArtilleryBusy; } class ERR_AirSupportPolicy { int Candidate; float ConsideringSince = -1; float NextDrop; string Reason = "waiting for delivered intelligence"; static string Refusal(ERR_AirSupportSituation s, bool checkAirspace = true) { if (!s || !s.AircraftReady) return "no ready helicopter passenger load"; if (!ERR_HQMemoryRules.Finite(s.Age) || !ERR_HQMemoryRules.Finite(s.Confidence) || !ERR_HQMemoryRules.Finite(s.Radius) || !ERR_HQMemoryRules.Finite(s.Speed)) return "invalid memory sample"; if (s.BeliefId <= 0 || s.Samples < 2 || s.Age < 0 || s.Age > 90 || s.Confidence < 0.7 || s.Confidence > 1 || s.Radius < 0 || s.Radius > 160) return "memory is too uncertain; continue reconnaissance"; if (!s.Infantry || !s.MotionKnown || s.Speed < 0 || s.Speed > 6) return "target movement does not justify an infantry drop"; int needed = Math.Clamp(s.Spotted * 2, 4, 12); if (s.CommittedFit >= needed) return "ground forces already committed in sufficient strength"; if (s.GroundFit + s.CommittedFit >= needed && s.ConfirmedLosses < 2) return "nearby ground forces can handle this contact"; if (checkAirspace && s.ArtilleryBusy) return "mortar order or shells occupy the approach area"; return ""; } bool Consider(ERR_AirSupportSituation s, float now) { if (!ERR_HQMemoryRules.Finite(now) || now < 0) return false; // Artillery temporarily closes the approach; it does not invalidate fresh // contact history or the confirmed lack of ground support. Restarting the // timer on every salvo could prevent a drop through every clear interval. string refusal = Refusal(s, false); if (!refusal.IsEmpty()) { Candidate = 0; ConsideringSince = -1; Reason = refusal; return false; } if (now < NextDrop) { Reason = "reinforcement cooldown; retain remaining troops"; return false; } if (Candidate != s.BeliefId || ConsideringSince < 0) { Candidate = s.BeliefId; ConsideringSince = now; } if (s.ArtilleryBusy) { Reason = Refusal(s); return false; } float delay = 45000; if (s.ConfirmedLosses >= 2) delay = 20000; if (now - ConsideringSince < delay) { Reason = "confirming persistent contact and ground-force shortfall"; return false; } Reason = "persistent infantry contact exceeds available ground response"; return true; } void Committed(float now) { NextDrop = now + 480000; Candidate = 0; ConsideringSince = -1; } void Retry(float now) { NextDrop = Math.Max(NextDrop, now + 15000); Candidate = 0; ConsideringSince = -1; } } // One immutable authorization; receiver revalidates memory and physical safety. class ERR_AirDropOrder { int BeliefId; int BeliefRevision; int MessageId; vector Target; float ExpiresAt; ref ERR_HQReport Report; }