// One mission-owned allocator. Reservations include every future native crew body. class ERR_ConvoyReceipt { int Id; string SourceId; bool Consumed; int OutstandingBodies = 19; int OutstandingGroups = 6; ref array Records = {-1, -1, -1, -1, -1, -1}; } class ERR_ConvoyPool { protected ERR_GameMode m_Mode; protected int m_Limit; protected ref array m_Receipts = {}; void ERR_ConvoyPool(ERR_GameMode mode, int maxConvoys) { m_Mode = mode; m_Limit = Math.Clamp(maxConvoys, 0, 8); } static int CrewCount(int vehicle) { if (vehicle == 1) return 1; if (vehicle == 2) return 9; if (vehicle == 4) return 3; if (vehicle == 0 || vehicle == 3 || vehicle == 5) return 2; return 0; } protected bool Current() { return Replication.IsServer() && m_Mode && m_Mode == ERR_GameMode.Get() && m_Mode.Active() && m_Mode.Forces(); } bool Owns(ERR_ConvoyReceipt receipt) { return receipt && m_Receipts.Contains(receipt); } int CommittedBodies() { if (!m_Mode || !m_Mode.Forces()) return 60; int total; ERR_ForceLedger ledger = m_Mode.Forces(); for (int i = 0; i < ledger.Count(); i++) { ERR_ForceRecord record = ledger.At(i); if (record && record.Physical && !record.Eliminated) total += record.Strength; } foreach (ERR_ConvoyReceipt receipt : m_Receipts) total += receipt.OutstandingBodies; return total; } protected int CommittedGroups() { int total = m_Mode.Forces().Count(); foreach (ERR_ConvoyReceipt receipt : m_Receipts) total += receipt.OutstandingGroups; return total; } ERR_ConvoyReceipt Reserve(string sourceId) { if (!Current() || sourceId.IsEmpty() || sourceId.Length() > 64) return null; foreach (ERR_ConvoyReceipt known : m_Receipts) if (known.SourceId == sourceId) return known; if (m_Receipts.Count() >= m_Limit || CommittedBodies() + 19 > 60 || CommittedGroups() + 6 > 128) return null; ERR_ConvoyReceipt receipt = new ERR_ConvoyReceipt(); receipt.Id = m_Receipts.Count() + 1; receipt.SourceId = sourceId; m_Receipts.Insert(receipt); return receipt; } bool Consume(ERR_ConvoyReceipt receipt) { if (!Current() || !Owns(receipt) || receipt.Consumed || CommittedBodies() > 60 || CommittedGroups() > 128) return false; receipt.Consumed = true; return true; } bool BindRecord(ERR_ConvoyReceipt receipt, int vehicle, ERR_ForceRecord record) { if (!Current() || !Owns(receipt) || !receipt.Consumed || vehicle < 0 || vehicle >= 6 || !record) return false; if (receipt.Records[vehicle] >= 0) return receipt.Records[vehicle] == record.Id; if (m_Mode.Forces().Find(record.Id) != record || record.Kind != ERR_ForceKind.CONVOY || !record.Group || !record.Physical || record.Eliminated) return false; if (record.SourceId != receipt.SourceId + "-vehicle-" + vehicle.ToString() || record.InitialStrength != CrewCount(vehicle) || record.Strength != record.InitialStrength) return false; receipt.Records[vehicle] = record.Id; receipt.OutstandingBodies -= CrewCount(vehicle); receipt.OutstandingGroups--; return true; } // There is intentionally no refund/reset method. A consumed startup attempt // can retain partial native entities even when a later seat or ledger step fails. }