// A local status snapshot is not HQ knowledge until its radio route delivers. // Count includes living unconscious members; only explicit observed deaths add // ConfirmedLosses. Neither a silent radio nor an absent member proves a death. class ERR_HQStatusReport { string SourceId; int MessageId; vector Position; int Strength; int Count; int Fit; int ConfirmedLosses; ref array DeathSlots = {}; float ObservedAt = -1; } class ERR_HQWatchRules { static bool Valid(ERR_HQStatusReport report, float now) { if (!report || report.SourceId.IsEmpty() || report.SourceId.Length() > 96 || report.MessageId <= 0) return false; if (!ERR_HQPursuitRules.PositionValid(report.Position) || !ERR_HQPursuitRules.Finite(report.ObservedAt) || !ERR_HQPursuitRules.Finite(now) || report.ObservedAt < 0 || now < report.ObservedAt || now - report.ObservedAt > 120000) return false; return report.Strength > 0 && report.Strength <= 32 && report.Count >= 0 && report.Count <= report.Strength && report.Fit >= 0 && report.Fit <= report.Count && report.ConfirmedLosses >= 0 && report.ConfirmedLosses <= report.Strength - report.Count; } static bool RequestDue(float observedAt, float askedAt, float nextAttempt, bool closed, float now) { return !closed && ERR_HQPursuitRules.Finite(now) && observedAt >= 0 && askedAt < 0 && now >= nextAttempt && now - observedAt >= 360000; } static bool InvestigationDue(float askedAt, float nextAttempt, bool closed, bool investigating, float now) { return !closed && !investigating && ERR_HQPursuitRules.Finite(now) && askedAt >= 0 && now >= nextAttempt && now - askedAt >= 30000; } static int SquadLimit(int baseline, int confirmedLosses) { if (baseline <= 0) return 0; if (baseline >= 8) return 8; // Confirmed losses increase the shared pool only. Individual hunts still // obey the independent pursuit cap of three and actual force availability. int losses = Math.Clamp(confirmedLosses, 0, 4096); int bonus = losses / 2; return Math.Min(8, baseline + bonus); } } // Detached HQ roster state: no entity/group handle and no live position lookup. // Entries returned by Find/At are borrowed for HQ inspection, never mutated by // observers. Only delivered Ingest and explicit request lifecycle methods write. class ERR_HQWatchEntry { int RequestId; string SourceId; int MessageId; vector Position; int Strength; int Count = -1; int Fit = -1; int ConfirmedLosses; ref ERR_HQDeliveredCasualties Casualties = new ERR_HQDeliveredCasualties(); float LastBodyDeliveredAt = -1; float ObservedAt = -1; float LastDeliveredAt = -1; float AskedAt = -1; float NextAttempt; bool Investigating; bool Closed; } class ERR_HQWatch { protected ref array m_Entries = {}; protected ref map m_BySource = new map(); protected int m_ConfirmedLosses; protected bool m_CapLogged; int Count() { return m_Entries.Count(); } ERR_HQWatchEntry Find(string sourceId) { return m_BySource.Get(sourceId); } ERR_HQWatchEntry At(int index) { if (index < 0 || index >= m_Entries.Count()) return null; return m_Entries[index]; } int ConfirmedLosses() { return m_ConfirmedLosses; } int SquadLimit(int baseline) { return ERR_HQWatchRules.SquadLimit(baseline, m_ConfirmedLosses); } protected ERR_HQWatchEntry Create(string sourceId, int strength) { // Closed identities remain tombstones: replays cannot recreate a formation. if (m_Entries.Count() >= 128) { if (!m_CapLogged) Print("Rereintroduced [watch]: 128-formation check-in cap reached."); m_CapLogged = true; return null; } ERR_HQWatchEntry entry = new ERR_HQWatchEntry(); entry.SourceId = sourceId; entry.Strength = strength; entry.Casualties.Bind(strength); m_Entries.Insert(entry); m_BySource.Set(sourceId, entry); return entry; } protected void ApplyLosses(ERR_HQWatchEntry entry) { int known = entry.Casualties.Count(); m_ConfirmedLosses += known - entry.ConfirmedLosses; entry.ConfirmedLosses = known; entry.Count = entry.Casualties.LivingUpperBound(entry.Count); if (entry.Count >= 0) entry.Fit = Math.Min(entry.Fit, entry.Count); entry.Closed = entry.Casualties.Closed(); if (entry.Closed) entry.Fit = 0; } bool Ingest(ERR_HQStatusReport report, float now) { if (!ERR_HQWatchRules.Valid(report, now)) return false; ERR_HQWatchEntry entry = Find(report.SourceId); if (entry && (report.MessageId <= entry.MessageId || report.ObservedAt <= entry.ObservedAt || now < entry.LastDeliveredAt || report.Strength != entry.Strength)) return false; if (!entry) { // Validate slot payload before allocation: malformed snapshots cannot consume the cap. ERR_HQDeliveredCasualties validation = new ERR_HQDeliveredCasualties(); if (!validation.Bind(report.Strength) || !validation.AcceptStatus(report.DeathSlots, report.ConfirmedLosses, report.Count)) return false; entry = Create(report.SourceId, report.Strength); if (!entry) return false; } if (!entry.Casualties.AcceptStatus(report.DeathSlots, report.ConfirmedLosses, report.Count)) return false; entry.MessageId = report.MessageId; entry.Position = report.Position; entry.Count = report.Count; entry.Fit = report.Fit; entry.ObservedAt = report.ObservedAt; entry.LastDeliveredAt = now; entry.AskedAt = -1; entry.NextAttempt = 0; entry.Investigating = false; ApplyLosses(entry); return true; } // Called only from delivered BODY intake; scalar evidence survives native target cleanup. // Corpse coordinates and body message IDs never replace a formation status/check-in. bool IngestBody(ERR_HQReport report, float now) { if (!report || report.TravelMode != "body" || report.Distress || report.MessageId <= 0 || report.CasualtySourceId.IsEmpty() || report.CasualtySourceId.Length() > 96 || report.CasualtyStrength < 1 || report.CasualtyStrength > 32 || report.CasualtySlot < 0 || report.CasualtySlot >= report.CasualtyStrength || !ERR_HQPursuitRules.Finite(now) || !ERR_HQPursuitRules.Finite(report.ObservedAt) || !ERR_HQPursuitRules.PositionValid(report.Position) || report.ObservedAt < 0 || now < report.ObservedAt || now - report.ObservedAt > 120000) return false; ERR_HQWatchEntry entry = Find(report.CasualtySourceId); if (entry && (entry.Strength != report.CasualtyStrength || now < entry.LastBodyDeliveredAt)) return false; if (!entry) entry = Create(report.CasualtySourceId, report.CasualtyStrength); if (!entry || !entry.Casualties.AcceptBody(report.CasualtySlot)) return false; entry.LastBodyDeliveredAt = now; ApplyLosses(entry); return true; } bool RequestDue(string sourceId, float now) { ERR_HQWatchEntry entry = Find(sourceId); return entry && ERR_HQWatchRules.RequestDue(entry.ObservedAt, entry.AskedAt, entry.NextAttempt, entry.Closed, now); } // Begin the deadline on HQ request attempt, including no route to a silent // patrol. A missing downlink cannot prevent an investigation indefinitely. bool MarkRequested(string sourceId, int statusMessageId, float now) { ERR_HQWatchEntry entry = Find(sourceId); if (!entry || entry.MessageId != statusMessageId || entry.RequestId >= 2147483646 || !RequestDue(sourceId, now)) return false; entry.RequestId++; entry.AskedAt = now; return true; } bool InvestigationDue(string sourceId, float now) { ERR_HQWatchEntry entry = Find(sourceId); return entry && ERR_HQWatchRules.InvestigationDue(entry.AskedAt, entry.NextAttempt, entry.Closed, entry.Investigating, now); } bool MarkInvestigating(string sourceId, int statusMessageId, int requestId, float now) { ERR_HQWatchEntry entry = Find(sourceId); if (!entry || entry.MessageId != statusMessageId || entry.RequestId != requestId || !InvestigationDue(sourceId, now)) return false; entry.Investigating = true; return true; } bool FinishInvestigation(string sourceId, int statusMessageId, int requestId, float now) { ERR_HQWatchEntry entry = Find(sourceId); if (!entry || entry.MessageId != statusMessageId || entry.RequestId != requestId || !entry.Investigating || !ERR_HQPursuitRules.Finite(now) || now < entry.AskedAt + 30000) return false; entry.Investigating = false; entry.AskedAt = -1; entry.NextAttempt = now + 180000; return true; } void Clear() { m_Entries.Clear(); m_BySource.Clear(); m_ConfirmedLosses = 0; m_CapLogged = false; } }