// Negative sightings have the same ownership as visual reports: HQ may ingest // this snapshot only after its physical source's uplink completes successfully. class ERR_HQSearchReport { string WatchSourceId; int WatchMessageId; int WatchRequestId; int SourceId; int MessageId; int BeliefId; int BeliefRevision; vector Position; float ObservedAt = -1; bool SweepCompleted; bool ContactFound; } class ERR_HQCoverageRules { static bool PositionValid(vector position) { return ERR_HQPursuitRules.PositionValid(position) && Math.AbsFloat(position[0]) < 1000000 && Math.AbsFloat(position[2]) < 1000000; } static int Cell(float coordinate) { return Math.Floor(coordinate / 500); } // Bounded coordinates make this integer grid key collision-free, including // negative positions. No per-candidate string or collection allocation. static int Key(vector position) { return (Cell(position[0]) + 2000) * 4096 + Cell(position[2]) + 2000; } static bool Recent(float searchedAt, float now) { return ERR_HQPursuitRules.Finite(searchedAt) && ERR_HQPursuitRules.Finite(now) && searchedAt >= 0 && now >= searchedAt && now - searchedAt <= 900000; } static bool ValidReport(int sourceId, int messageId, int beliefId, int revision, bool completed, bool found, float observedAt, float now) { return sourceId > 0 && messageId > 0 && beliefId > 0 && revision > 0 && completed && !found && Recent(observedAt, now) && now - observedAt <= 120000; } static bool Point(int x, int z, vector center, float radius, out vector point) { point = Vector(x * 500 + 250, center[1], z * 500 + 250); if (vector.DistanceXZ(point, center) > radius) { // Small uncertainty circles need not include a grid center. Choose a point // in their intersecting cell; inset edges keep its key in the selected cell. point[0] = Math.Clamp(center[0], x * 500, x * 500 + 499.99); point[2] = Math.Clamp(center[2], z * 500, z * 500 + 499.99); } return PositionValid(point) && vector.DistanceXZ(point, center) <= radius; } } // A cell records a reported sweep visit, not proof that every building or hiding // place in its 500m square was visible. New positive sightings invalidate that // coarse exclusion; replay watermarks survive so old negatives cannot restore it. class ERR_HQCoverage { protected ref map m_Cells = new map(); protected ref map m_Messages = new map(); int Count() { return m_Cells.Count(); } void Forget(vector position) { if (ERR_HQCoverageRules.PositionValid(position)) m_Cells.Remove(ERR_HQCoverageRules.Key(position)); } bool Recent(vector position, float now) { if (!ERR_HQCoverageRules.PositionValid(position)) return false; int key = ERR_HQCoverageRules.Key(position); return m_Cells.Contains(key) && ERR_HQCoverageRules.Recent(m_Cells.Get(key), now); } bool Ingest(ERR_HQSearchReport report, float now) { if (!report || !ERR_HQCoverageRules.PositionValid(report.Position)) return false; if (!ERR_HQCoverageRules.ValidReport(report.SourceId, report.MessageId, report.BeliefId, report.BeliefRevision, report.SweepCompleted, report.ContactFound, report.ObservedAt, now)) return false; if (m_Messages.Contains(report.SourceId) && report.MessageId <= m_Messages.Get(report.SourceId)) return false; // One uplink per source gives monotonic message order. Never evict replay // watermarks; the mission already caps its total physical formations at 128. if (!m_Messages.Contains(report.SourceId) && m_Messages.Count() >= 128) return false; m_Messages.Set(report.SourceId, report.MessageId); int key = ERR_HQCoverageRules.Key(report.Position); if (m_Cells.Contains(key) && m_Cells.Get(key) >= report.ObservedAt) return false; if (!m_Cells.Contains(key) && m_Cells.Count() >= 128) { int oldestKey; float oldest = 1e20; foreach (int known, float searched : m_Cells) { if (searched >= oldest) continue; oldest = searched; oldestKey = known; } if (oldest >= report.ObservedAt) return false; m_Cells.Remove(oldestKey); } // Store the observation time, not delivery time: radio delay never makes a // searched area newer than the physical sweep that justified it. m_Cells.Set(key, report.ObservedAt); return true; } protected bool Reserved(vector point, array reserved) { if (!reserved) return false; int key = ERR_HQCoverageRules.Key(point); foreach (vector position : reserved) if (ERR_HQCoverageRules.PositionValid(position) && ERR_HQCoverageRules.Key(position) == key) return true; return false; } bool Select(vector center, float radius, vector fromPosition, float now, out vector destination, array reserved = null) { destination = center; if (!ERR_HQCoverageRules.PositionValid(center) || !ERR_HQCoverageRules.PositionValid(fromPosition)) return false; if (!ERR_HQPursuitRules.Finite(now) || now < 0 || !ERR_HQPursuitRules.Finite(radius) || radius < 0 || radius > 2000 || (reserved && reserved.Count() > 8)) return false; int lowX = ERR_HQCoverageRules.Cell(center[0] - radius); int highX = ERR_HQCoverageRules.Cell(center[0] + radius); int lowZ = ERR_HQCoverageRules.Cell(center[2] - radius); int highZ = ERR_HQCoverageRules.Cell(center[2] + radius); float best = 1e20; bool found; vector point; // Radius is bounded to 2 km: at most nine by nine candidate grid cells. for (int x = lowX; x <= highX; x++) { for (int z = lowZ; z <= highZ; z++) { if (!ERR_HQCoverageRules.Point(x, z, center, radius, point)) continue; if (Recent(point, now) || Reserved(point, reserved)) continue; float distance = vector.DistanceXZ(point, fromPosition); if (distance >= best) continue; best = distance; destination = point; found = true; } } return found; } void Clear() { m_Cells.Clear(); m_Messages.Clear(); } }