// Mission-owned finite attempt history. Registration and assignment release // never reset it. Values are copied before native work; no live enemy queries. class ERR_EngineerMinePost { ref ERR_ForceRecord Record; SCR_AIGroup Group; string Source; int Strength; ref ERR_HQTask Task; ref ERR_HQReport Assignment; int MessageId; int BeliefId; int Revision; float QueuedAt; float ObservedAt; vector Post; bool HasSite; vector Site; } class ERR_EngineerMinePosts { protected ref array m_Posts = {}; int Count() { return m_Posts.Count(); } bool Eligible(ERR_ForceRecord record, string source, ERR_HQTask task, ERR_HQReport assignment, float now) { if (!record || !record.Group || !record.Physical || record.Eliminated || source.IsEmpty() || record.SourceId != source) return false; if (record.InitialStrength < 1 || record.InitialStrength > 32 || !task || !assignment || m_Posts.Count() >= 32) return false; if (!task.RoadIntercept || task.ReleaseOnly || task.Investigation() || !task.SecuritySite.IsEmpty()) return false; if (task.BeliefId <= 0 || task.BeliefRevision <= 0 || assignment.MessageId <= 0) return false; if (!ERR_ForceRules.Finite(now) || !ERR_ForceRules.Finite(task.QueuedAt) || !ERR_ForceRules.Finite(task.ExpiresAt) || !ERR_ForceRules.Finite(assignment.ObservedAt) || assignment.ObservedAt < 0 || task.QueuedAt < assignment.ObservedAt || task.QueuedAt > now || now >= task.ExpiresAt) return false; if (!ERR_ForceRules.FiniteVector(task.Destination) || !ERR_ForceRules.FiniteVector(assignment.Position)) return false; int used; foreach (ERR_EngineerMinePost previous : m_Posts) { if (!previous) return false; float separation = vector.DistanceXZ(task.Destination, previous.Post); if (!ERR_ForceRules.Finite(separation) || separation <= 160) return false; if (previous.Task == task || previous.Assignment == assignment) return false; // Same source or physical group under another record cannot acquire quota. if (previous.Record != record) { if (previous.Source == source || previous.Group == record.Group) return false; continue; } if (previous.Source != source || previous.Group != record.Group || previous.Strength != record.InitialStrength) return false; used++; if (used >= 5 || task.QueuedAt <= previous.QueuedAt || assignment.ObservedAt <= previous.ObservedAt || assignment.MessageId == previous.MessageId) return false; if (task.BeliefId == previous.BeliefId && task.BeliefRevision <= previous.Revision) return false; } return true; } // Returns immutable entry index or -1; a refused site/item still consumes it. int Consume(ERR_ForceRecord record, string source, ERR_HQTask task, ERR_HQReport assignment, float now) { if (!Eligible(record, source, task, assignment, now)) return -1; ERR_EngineerMinePost entry = new ERR_EngineerMinePost(); entry.Record = record; entry.Group = record.Group; entry.Source = source; entry.Strength = record.InitialStrength; entry.Task = task; entry.Assignment = assignment; entry.MessageId = assignment.MessageId; entry.BeliefId = task.BeliefId; entry.Revision = task.BeliefRevision; entry.QueuedAt = task.QueuedAt; entry.ObservedAt = assignment.ObservedAt; entry.Post = task.Destination; m_Posts.Insert(entry); return m_Posts.Count() - 1; } bool RecordSite(int index, vector site) { if (index < 0 || index >= m_Posts.Count() || !ERR_ForceRules.FiniteVector(site)) return false; ERR_EngineerMinePost entry = m_Posts[index]; if (!entry || entry.HasSite || !ERR_EngineerMineSite.InBand(entry.Post, site)) return false; entry.Site = site; entry.HasSite = true; return true; } void PriorSites(out array sites) { sites.Clear(); foreach (ERR_EngineerMinePost entry : m_Posts) if (entry.HasSite) sites.Insert(entry.Site); } }