Files
EscapistsReintroduced/docs/evidence/supply-travel-2026-10-03/probe.patch
T
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

260 lines
12 KiB
Diff

--- /dev/null
+++ b/addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSupplyTravelProbe.c
@@ -0,0 +1,256 @@
+#ifdef WORKBENCH
+// Observation-only qualification of one naturally admitted production convoy.
+// Baselines are taken at first ready observation, not at vehicle spawn.
+class ERR_NativeSupplyTravelFormation
+{
+ ERR_ConvoyVehicle Escort;
+ ERR_SupplyTeam Team;
+ Vehicle Root;
+ SCR_AIGroup Group;
+ IEntity Driver;
+ ref array<IEntity> Members = {};
+ ref array<BaseCompartmentSlot> Seats = {};
+ ERR_ForceRecord Record;
+ int RecordId;
+ vector Start;
+ bool Moved;
+}
+
+class ERR_NativeSupplyTravelProbe
+{
+ protected ERR_GameMode m_Mode;
+ protected ERR_HQ m_HQ;
+ protected ERR_SupplyConvoyService m_Service;
+ protected ref ERR_SupplyConvoyTask m_Task;
+ protected ref ERR_ConvoyReceipt m_Receipt;
+ protected string m_SourceId;
+ protected int m_ReceiptId;
+ protected int m_TaskIndex = -1;
+ protected ref array<ref ERR_NativeSupplyTravelFormation> m_Formations = {};
+ protected float m_AdmissionDeadline;
+ protected float m_TravelDeadline;
+ protected float m_LastNow;
+ protected float m_NextLog;
+ protected bool m_Stopped;
+ protected bool m_Terminal;
+
+ void ERR_NativeSupplyTravelProbe(ERR_GameMode mode, float now)
+ {
+ m_Mode = mode;
+ m_LastNow = now;
+ m_AdmissionDeadline = now + 120000;
+ if (!ERR_ForceRules.Finite(now) || now < 0) m_Stopped = true;
+ }
+ protected int Fail(string reason, int slot = -1)
+ {
+ if (!m_Terminal && !m_Stopped)
+ {
+ PrintFormat("ERR SUPPLY TRAVEL FAIL: task=%1 slot=%2 reason=%3", m_TaskIndex, slot, reason);
+ if (m_Task)
+ {
+ int tactics = -1;
+ if (m_Task.Tactics) tactics = m_Task.Tactics.State();
+ PrintFormat("ERR SUPPLY TRAVEL TASK: source=%1 ready=%2 stopped=%3 reason=%4 tactics=%5; observation does not establish cause", m_SourceId, m_Task.Ready, m_Task.Stopped, m_Task.Reason, tactics);
+ }
+ if (slot >= 0 && slot < 6 && slot < m_Formations.Count())
+ {
+ ERR_NativeSupplyTravelFormation saved = m_Formations[slot];
+ bool alive = ERR_Actors.Alive(saved.Root);
+ bool fit = ERR_Actors.CanFight(saved.Driver);
+ bool group = saved.Driver && ERR_Actors.Group(saved.Driver) == saved.Group;
+ bool aboard = saved.Driver && ERR_ConvoyRules.PartOf(CompartmentAccessComponent.GetVehicleIn(saved.Driver), saved.Root);
+ bool released;
+ if (saved.Escort) released = saved.Escort.Released;
+ PrintFormat("ERR SUPPLY TRAVEL SLOT: rootAlive=%1 driverFit=%2 driverOriginalGroup=%3 driverAboard=%4 released=%5", alive, fit, group, aboard, released);
+ }
+ }
+ m_Terminal = true;
+ return -1;
+ }
+ protected bool Owner(ERR_HQ hq)
+ {
+ if (!Replication.IsServer() || !m_Mode || m_Mode != ERR_GameMode.Get() || !m_Mode.Active() || !m_Mode.Forces()) return false;
+ if (!hq || m_Mode.Command() != hq || !hq.SupplyConvoys()) return false;
+ ERR_SupplyConvoyService service = ERR_SupplyConvoyService.Cast(hq.Convoys());
+ if (!service) return false;
+ if (m_HQ && (m_HQ != hq || m_Service != service)) return false;
+ return true;
+ }
+ protected bool RecordCurrent(ERR_NativeSupplyTravelFormation saved, int slot)
+ {
+ if (!saved.Group || saved.Group.GetFaction() != m_Mode.EnemyFaction() || saved.Group.GetPlayerCount(true) > 0) return false;
+ ERR_ForceRecord record = m_Mode.Forces().FindGroup(saved.Group);
+ if (!record || record != saved.Record || record.Id != saved.RecordId || record.Id != m_Receipt.Records[slot]) return false;
+ return record.Physical && !record.Eliminated && record.Group == saved.Group && record.Kind == ERR_ForceKind.CONVOY
+ && record.SourceId == m_SourceId + "-vehicle-" + slot.ToString();
+ }
+ protected bool RosterCurrent(ERR_NativeSupplyTravelFormation saved, array<IEntity> members, array<BaseCompartmentSlot> seats)
+ {
+ if (members.Count() != saved.Members.Count() || seats.Count() != saved.Seats.Count()) return false;
+ array<AIAgent> agents = {}; saved.Group.GetAgents(agents);
+ if (agents.Count() != saved.Members.Count()) return false;
+ array<IEntity> observed = {};
+ foreach (AIAgent agent : agents)
+ {
+ if (!agent) return false;
+ IEntity actor = agent.GetControlledEntity();
+ if (!actor || !saved.Members.Contains(actor) || observed.Contains(actor)) return false;
+ observed.Insert(actor);
+ }
+ for (int i = 0; i < saved.Members.Count(); i++)
+ {
+ IEntity original = saved.Members[i];
+ if (!original || members[i] != original || !saved.Seats[i] || seats[i] != saved.Seats[i]) return false;
+ if (!ERR_Actors.Alive(original) || ERR_Actors.Player(original) || ERR_Actors.Group(original) != saved.Group) return false;
+ }
+ return true;
+ }
+ protected int Validate()
+ {
+ if (!m_Task || m_Task.Stopped || !m_Task.Ready || m_Task.Receipt != m_Receipt) return Fail("original ready task lost");
+ if (!m_Receipt || !m_Receipt.Consumed || m_Receipt.Id != m_ReceiptId || m_Receipt.SourceId != m_SourceId) return Fail("original receipt changed");
+ if (m_Receipt.Records.Count() != 8 || m_Receipt.OutstandingBodies != 0 || m_Receipt.OutstandingGroups != 0) return Fail("original ledger admission changed");
+ if (m_Task.Vehicles.Count() != 6 || m_Task.Teams.Count() != 2 || m_Formations.Count() != 8) return Fail("original formation count changed");
+ for (int slot = 0; slot < 8; slot++)
+ {
+ ERR_NativeSupplyTravelFormation saved = m_Formations[slot];
+ if (!RecordCurrent(saved, slot)) return Fail("original force record or group lost", slot);
+ if (slot < 6)
+ {
+ ERR_ConvoyVehicle escort = m_Task.Vehicles[slot];
+ if (!escort || escort != saved.Escort || escort.Entity != saved.Root || escort.Group != saved.Group
+ || escort.Index != slot || escort.Driver != saved.Driver || escort.Released || !escort.Registered) return Fail("original vehicle ownership changed", slot);
+ if (!ERR_Actors.Alive(saved.Root) || ERR_ConvoyRules.Captured(saved.Root, saved.Group)) return Fail("original vehicle lost or captured", slot);
+ if (!ERR_ForceRules.FiniteVector(saved.Root.GetOrigin())) return Fail("nonfinite original vehicle position", slot);
+ if (!RosterCurrent(saved, escort.Members, escort.Seats)) return Fail("original vehicle roster or seat identity changed", slot);
+ if (!ERR_Actors.CanFight(saved.Driver) || !ERR_ConvoyRules.PartOf(CompartmentAccessComponent.GetVehicleIn(saved.Driver), saved.Root)) return Fail("original driver not fit and aboard", slot);
+ int driverSlot = saved.Members.Find(saved.Driver);
+ if (driverSlot < 0 || saved.Seats[driverSlot].GetOccupant() != saved.Driver) return Fail("original driver seat lost", slot);
+ }
+ else
+ {
+ ERR_SupplyTeam team = m_Task.Teams[slot - 6];
+ if (!team || team != saved.Team || team.Group != saved.Group || team.Released || !team.Registered) return Fail("original infantry team lost", slot);
+ if (!RosterCurrent(saved, team.Members, team.Seats)) return Fail("original infantry roster or seat identity changed", slot);
+ }
+ }
+ return 0;
+ }
+ protected int Pin(ERR_SupplyConvoyTask task, int index, float now)
+ {
+ m_Task = task; m_TaskIndex = index;
+ m_Receipt = task.Receipt;
+ if (!m_Receipt || m_Receipt.Records.Count() != 8 || task.Vehicles.Count() != 6 || task.Teams.Count() != 2) return Fail("first ready production task has invalid shape");
+ m_SourceId = m_Receipt.SourceId; m_ReceiptId = m_Receipt.Id;
+ array<IEntity> originals = {};
+ array<SCR_AIGroup> groups = {};
+ array<BaseCompartmentSlot> originalSeats = {};
+ array<Vehicle> roots = {};
+ for (int slot = 0; slot < 8; slot++)
+ {
+ ERR_NativeSupplyTravelFormation saved = new ERR_NativeSupplyTravelFormation();
+ m_Formations.Insert(saved);
+ if (slot < 6)
+ {
+ saved.Escort = task.Vehicles[slot];
+ if (!saved.Escort) return Fail("missing original vehicle", slot);
+ saved.Root = saved.Escort.Entity; saved.Group = saved.Escort.Group; saved.Driver = saved.Escort.Driver;
+ saved.Members.Copy(saved.Escort.Members); saved.Seats.Copy(saved.Escort.Seats);
+ if (!saved.Root || roots.Contains(saved.Root)) return Fail("missing or duplicate original vehicle", slot);
+ roots.Insert(saved.Root); saved.Start = saved.Root.GetOrigin();
+ if (!ERR_ForceRules.FiniteVector(saved.Start)) return Fail("invalid ready-position baseline", slot);
+ int expected = 2;
+ if (slot == 1 || slot == 2) expected = 1;
+ if (slot == 4) expected = 3;
+ if (saved.Members.Count() != expected) return Fail("unexpected original vehicle crew count", slot);
+ }
+ else
+ {
+ saved.Team = task.Teams[slot - 6];
+ if (!saved.Team) return Fail("missing original infantry team", slot);
+ saved.Group = saved.Team.Group; saved.Members.Copy(saved.Team.Members); saved.Seats.Copy(saved.Team.Seats);
+ if (saved.Members.Count() != 7) return Fail("unexpected original infantry count", slot);
+ }
+ if (!saved.Group || groups.Contains(saved.Group)) return Fail("missing or shared original group", slot);
+ groups.Insert(saved.Group);
+ if (saved.Seats.Count() != saved.Members.Count()) return Fail("original seat count differs", slot);
+ foreach (BaseCompartmentSlot seat : saved.Seats)
+ {
+ if (!seat || originalSeats.Contains(seat)) return Fail("missing or duplicated original seat", slot);
+ originalSeats.Insert(seat);
+ }
+ foreach (IEntity original : saved.Members)
+ {
+ if (!original || originals.Contains(original)) return Fail("missing or duplicated original actor", slot);
+ originals.Insert(original);
+ }
+ saved.Record = m_Mode.Forces().FindGroup(saved.Group);
+ if (!saved.Record) return Fail("original force record absent", slot);
+ saved.RecordId = saved.Record.Id;
+ }
+ if (originals.Count() != 25) return Fail("expected exactly25 original crew and infantry");
+ if (Validate() < 0) return -1;
+ m_TravelDeadline = now + 180000;
+ PrintFormat("ERR SUPPLY TRAVEL ADMITTED: task=%1 source=%2 six vehicles and25 original actors; baseline=first-ready-observation deadline=%3", index, m_SourceId, m_TravelDeadline);
+ return 0;
+ }
+ int Tick(ERR_HQ hq, float now)
+ {
+ if (m_Stopped || m_Terminal) return -1;
+ if (!ERR_ForceRules.Finite(now) || now < m_LastNow) return Fail("invalid or reversed observation clock");
+ m_LastNow = now;
+ if (!Owner(hq)) return Fail("current production mission or service unavailable");
+ if (!m_HQ) { m_HQ = hq; m_Service = ERR_SupplyConvoyService.Cast(hq.Convoys()); }
+ int count = m_Service.Count();
+ if (count < 0 || count > 20) return Fail("production task count outside configured bound");
+ if (!m_Task)
+ {
+ if (now >= m_AdmissionDeadline) return Fail("admission deadline; no ready production convoy observed");
+ for (int index = 0; index < count; index++)
+ {
+ ERR_SupplyConvoyTask task = ERR_SupplyConvoyTask.Cast(m_Service.At(index));
+ if (!task) return Fail("nonproduction convoy in production service");
+ if (!task.Ready) continue;
+ return Pin(task, index, now);
+ }
+ if (now >= m_NextLog)
+ {
+ PrintFormat("ERR SUPPLY TRAVEL ADMISSION: ready convoy pending; %1", hq.ConvoyStatus());
+ m_NextLog = now + 10000;
+ }
+ return 0;
+ }
+ if (m_TaskIndex < 0 || m_TaskIndex >= count || m_Service.At(m_TaskIndex) != m_Task) return Fail("original task no longer at pinned service slot");
+ if (now >= m_TravelDeadline) return Fail("travel deadline before all six originals moved20m");
+ if (Validate() < 0) return -1;
+ int moved;
+ for (int slot = 0; slot < 6; slot++)
+ {
+ ERR_NativeSupplyTravelFormation saved = m_Formations[slot];
+ if (!saved.Moved && vector.DistanceXZ(saved.Root.GetOrigin(), saved.Start) >= 20)
+ {
+ saved.Moved = true;
+ PrintFormat("ERR SUPPLY TRAVEL MOVED: task=%1 slot=%2 original entity travelled20m from first-ready baseline", m_TaskIndex, slot);
+ }
+ if (saved.Moved) moved++;
+ }
+ if (moved == 6)
+ {
+ m_Terminal = true;
+ Print("ERR SUPPLY TRAVEL PASS: all six original production vehicles moved20m from first-ready baselines with original living crews and seated drivers; no tactics or full-route completion claim");
+ return 1;
+ }
+ if (now >= m_NextLog)
+ {
+ PrintFormat("ERR SUPPLY TRAVEL PROGRESS: task=%1 moved=%2/6 fixedDeadline=%3", m_TaskIndex, moved, m_TravelDeadline);
+ m_NextLog = now + 10000;
+ }
+ return 0;
+ }
+ void Stop()
+ {
+ m_Stopped = true;
+ m_Formations.Clear(); m_Task = null; m_Receipt = null; m_HQ = null; m_Service = null; m_Mode = null;
+ }
+}
+#endif