Files
EscapistsReintroduced/addons/chat/Scripts/Game/ERR/ERR_NativeConvoyCoverProbe.c
T

252 lines
12 KiB
C

#ifdef WORKBENCH
// Opt-in controlled arrangement of the existing local prisoner only. Production
// perception, radio intake, planning and downlink execution remain unchanged.
modded class ERR_HQ
{
protected override void Observe(ERR_HQUnit unit, float now)
{
ERR_HQReport before = unit.PendingReport;
super.Observe(unit, now);
ERR_NativeConvoyCoverProbe proof = ERR_NativeConvoyCoverProbe.Active();
if (proof && unit.PendingReport && unit.PendingReport != before) proof.Observed(unit, now);
}
protected override void Accept(ERR_HQReport report, float now)
{
super.Accept(report, now);
ERR_NativeConvoyCoverProbe proof = ERR_NativeConvoyCoverProbe.Active();
if (!proof || !report) return;
foreach (ERR_HQReport accepted : m_Contacts)
if (accepted.MessageId == report.MessageId) { proof.Accepted(accepted, now); break; }
}
protected override void DeliverConvoyOrder(ERR_HQUnit unit, float now)
{
ERR_ConvoyCombatOrder order = unit.PendingConvoyOrder;
ERR_HQRoute route = unit.Downlink;
bool hadWaypoint;
ERR_ConvoyTask task = ConvoyTask();
if (task && !task.Vehicles.IsEmpty()) hadWaypoint = task.Vehicles[0].Waypoint != null;
super.DeliverConvoyOrder(unit, now);
ERR_NativeConvoyCoverProbe proof = ERR_NativeConvoyCoverProbe.Active();
if (proof && order && route) proof.Delivered(order, route, hadWaypoint, now);
}
}
class ERR_NativeConvoyCoverReport
{
ref ERR_HQReport Report;
ref ERR_HQRoute Uplink;
bool Accepted;
}
class ERR_NativeConvoyCoverProbe
{
protected static ERR_NativeConvoyCoverProbe s_Active;
protected ERR_GameMode m_Mode;
protected ERR_HQ m_HQ;
protected ERR_ConvoyTask m_Task;
protected ERR_ConvoyVehicle m_Lead;
protected IEntity m_Player;
protected SCR_PlayerController m_Controller;
protected ERR_PlayerLife m_Life;
protected DamageManagerComponent m_Damage;
protected bool m_DamageWasEnabled;
protected bool m_Protected;
protected bool m_Stopped;
protected bool m_Failed;
protected bool m_Held;
protected bool m_Resumed;
protected int m_RouteIndex;
protected int m_LedgerCount;
protected float m_Deadline;
protected float m_HeldAt;
protected float m_ResumedAt;
protected float m_NextLog;
protected vector m_HoldPosition;
protected vector m_ResumePosition;
protected vector m_HidePosition;
protected ref array<IEntity> m_Forces = {};
protected ref array<ref ERR_NativeConvoyCoverReport> m_Reports = {};
protected int m_Step;
static bool Enabled()
{
string flag;
return ERR_NativeConvoyProbe.Enabled() && System.GetCLIParam("errConvoyCover", flag) && flag == "1";
}
static ERR_NativeConvoyCoverProbe Active() { return s_Active; }
void ERR_NativeConvoyCoverProbe(ERR_GameMode mode) { m_Mode = mode; }
protected bool Check(bool condition, string detail)
{
if (condition) { Print("ERR SMOKE PASS: convoy cover " + detail); return true; }
m_Failed = true; Print("ERR SMOKE FAIL: convoy cover " + detail, LogLevel.ERROR); Stop(); return false;
}
protected bool Canonical()
{
return m_Player && m_Controller && m_Life && m_Mode.Players().CurrentActor(m_Player) == m_Life
&& m_Controller == GetGame().GetPlayerController() && m_Controller.GetControlledEntity() == m_Player
&& m_Life.Actor == m_Player && m_Life.Connected && m_Life.UsedLife;
}
protected bool UnchangedState()
{
if (!Canonical() || !m_Task || m_Task.Stopped || m_Task.Vehicles.Count() != 6
|| m_Mode.Forces().Count() != m_LedgerCount || m_Task.Cargo.Count() != 56) return false;
foreach (ERR_ConvoyVehicle vehicle : m_Task.Vehicles)
if (!vehicle || vehicle.Released || !vehicle.CrewValid(m_Mode)) return false;
array<IEntity> current = {}; m_HQ.PhysicalForces(current);
if (current.Count() != m_Forces.Count()) return false;
foreach (IEntity entity : current) if (!m_Forces.Contains(entity)) return false;
return true;
}
protected bool Unchanged()
{
if (UnchangedState()) return true;
// Read-only, failure-only evidence. Preserve the original invariant result.
PrintFormat("ERR CONVOY COVER INVARIANT: canonical=%1 player=%2 controller=%3 life=%4 ledger=%5 expectedLedger=%6",
Canonical(), m_Player != null, m_Controller != null, m_Life != null, m_Mode.Forces().Count(), m_LedgerCount);
if (!m_Task) { Print("ERR CONVOY COVER INVARIANT: missing original task"); return false; }
PrintFormat("ERR CONVOY COVER INVARIANT: stopped=%1 reason=%2 ready=%3 vehicles=%4 cargoCallbacks=%5",
m_Task.Stopped, m_Task.Reason, m_Task.Ready, m_Task.Vehicles.Count(), m_Task.Cargo.Count());
for (int index = 0; index < m_Task.Vehicles.Count() && index < 6; index++)
{
ERR_ConvoyVehicle vehicle = m_Task.Vehicles[index];
if (!vehicle) { PrintFormat("ERR CONVOY COVER INVARIANT: missing vehicle record=%1", index); continue; }
bool crewValid = vehicle.CrewValid(m_Mode);
PrintFormat("ERR CONVOY COVER INVARIANT: vehicle=%1 released=%2 arrived=%3 crewValid=%4 entityAlive=%5 group=%6 routeIndex=%7 waypoint=%8",
index, vehicle.Released, vehicle.Arrived, crewValid, ERR_Actors.Alive(vehicle.Entity), vehicle.Group, vehicle.RouteIndex, vehicle.Waypoint != null);
if (crewValid) continue;
IEntity driverVehicle;
if (vehicle.Driver) driverVehicle = CompartmentAccessComponent.GetVehicleIn(vehicle.Driver);
PrintFormat("ERR CONVOY COVER INVARIANT: vehicle=%1 driverConscious=%2 driverPlayer=%3 driverAboardOriginal=%4",
index, ERR_Actors.CanFight(vehicle.Driver), ERR_Actors.Player(vehicle.Driver),
ERR_ConvoyRules.PartOf(driverVehicle, vehicle.Entity));
for (int memberIndex = 0; memberIndex < vehicle.Members.Count() && memberIndex < 19; memberIndex++)
{
IEntity member = vehicle.Members[memberIndex];
PrintFormat("ERR CONVOY COVER INVARIANT: vehicle=%1 member=%2 actor=%3 alive=%4 player=%5 sameGroup=%6",
index, memberIndex, member, ERR_Actors.Alive(member), ERR_Actors.Player(member), ERR_Actors.Group(member) == vehicle.Group);
}
}
array<IEntity> current = {}; m_HQ.PhysicalForces(current);
PrintFormat("ERR CONVOY COVER INVARIANT: currentForces=%1 initialForces=%2", current.Count(), m_Forces.Count());
int differences;
foreach (IEntity added : current)
if (!m_Forces.Contains(added) && differences++ < 8) PrintFormat("ERR CONVOY COVER INVARIANT: newly listed physical entity=%1", added);
differences = 0;
foreach (IEntity missing : m_Forces)
if (!current.Contains(missing) && differences++ < 8) PrintFormat("ERR CONVOY COVER INVARIANT: no longer listed physical entity=%1", missing);
return false;
}
protected void MovePrisoner(vector position)
{
vector transform[4]; m_Player.GetWorldTransform(transform);
transform[3] = ERR_Actors.Ground(position);
ChimeraCharacter.Cast(m_Player).Teleport(transform);
}
protected ERR_NativeConvoyCoverReport Find(int id)
{
foreach (ERR_NativeConvoyCoverReport entry : m_Reports)
if (entry.Report.MessageId == id) return entry;
return null;
}
void Observed(ERR_HQUnit unit, float now)
{
if (m_Stopped || !unit.PendingReport || unit.PendingReport.Target != m_Player || !unit.Uplink) return;
IEntity observer = unit.Utility.ERR_ResponseVisualObserver(unit.PendingReport, now);
if (!observer || !m_Forces.Contains(observer) || m_Reports.Count() >= 64) return;
ERR_NativeConvoyCoverReport entry = new ERR_NativeConvoyCoverReport();
entry.Report = unit.PendingReport.Snapshot(); entry.Uplink = unit.Uplink; m_Reports.Insert(entry);
}
void Accepted(ERR_HQReport report, float now)
{
if (m_Stopped) return;
ERR_NativeConvoyCoverReport entry = Find(report.MessageId);
if (!entry) return;
entry.Accepted = !entry.Uplink.Failed && entry.Uplink.StartedAt >= 0
&& entry.Uplink.Cursor >= entry.Uplink.Nodes.Count();
}
void Delivered(ERR_ConvoyCombatOrder order, ERR_HQRoute route, bool hadWaypoint, float now)
{
if (m_Stopped || !m_Lead || order.Group != m_Lead.Group
|| m_Lead.Combat.LastCommandId != order.CommandId || route.Failed) return;
if (!Check(route.Downlink && route.StartedAt >= 0 && route.Cursor == route.Nodes.Count() - 1,
"original crew received its real completed downlink")) return;
if (order.Hold)
{
ERR_NativeConvoyCoverReport entry = Find(order.ReportId);
if (!Check(entry && entry.Accepted && entry.Report.Target == m_Player,
"HOLD traces to actual original observer perception and delivered sighting")) return;
if (m_Held) return;
if (!Check(hadWaypoint && m_Lead.Combat.Held && !m_Lead.Waypoint,
"delivered HOLD removed the owned native road waypoint")) return;
m_Held = true; m_HeldAt = now; m_RouteIndex = m_Lead.RouteIndex;
m_HoldPosition = m_Lead.Entity.GetOrigin();
}
else
{
if (!Check(m_Held && !hadWaypoint && !m_Lead.Combat.Held && m_Lead.Waypoint
&& m_Lead.RouteIndex == m_RouteIndex && now - order.ObservedAt >= 60000,
"delivered RESUME restores one road waypoint at the preserved index after quiet evidence")) return;
m_Resumed = true; m_ResumedAt = now; m_ResumePosition = m_Lead.Entity.GetOrigin();
}
}
int Tick(ERR_HQ hq, float now)
{
if (!Enabled() || !hq || m_Failed || m_Stopped) return -1;
if (m_Step == 0)
{
m_HQ = hq; m_Task = hq.ConvoyTask();
m_Controller = SCR_PlayerController.Cast(GetGame().GetPlayerController());
if (m_Controller) m_Player = m_Controller.GetControlledEntity();
m_Life = m_Mode.Players().CurrentActor(m_Player);
if (!Check(Canonical() && m_Task && m_Task.Ready && m_Task.Vehicles.Count() == 6,
"canonical local prisoner and previously proven physical convoy selected")) return -1;
m_Lead = m_Task.Vehicles[0];
ERR_SiteRuntime hide = m_Mode.Sites().Find("everon-extraction-1");
if (!Check(hide && hide.Definition && !m_Lead.Combat.Held && m_Lead.Waypoint
&& vector.DistanceXZ(hide.Definition.Position, m_Lead.Entity.GetOrigin()) >= 800,
"lead has a live road order and authored distant concealment destination")) return -1;
m_HidePosition = hide.Definition.Position;
m_Damage = DamageManagerComponent.Cast(m_Player.FindComponent(DamageManagerComponent));
if (!Check(m_Damage != null, "existing prisoner native damage component found")) return -1;
m_DamageWasEnabled = m_Damage.IsDamageHandlingEnabled(); m_Damage.EnableDamageHandling(false); m_Protected = true;
m_HQ.PhysicalForces(m_Forces); m_LedgerCount = m_Mode.Forces().Count();
vector vehicleTransform[4]; m_Lead.Entity.GetWorldTransform(vehicleTransform);
s_Active = this; m_Deadline = now + 300000; m_Step = 1;
MovePrisoner(m_Lead.Entity.GetOrigin() + vehicleTransform[0] * 20);
Print("ERR CONVOY COVER arrangement: existing prisoner moved beside original lead; temporary damage protection; no AI, inventory, reports, timers or radio routes changed.");
}
if (!Unchanged()) { Check(false, "original physical forces or canonical player changed"); return -1; }
if (now >= m_Deadline) { Check(false, "bounded sighting/hold/resume deadline"); return -1; }
if (now >= m_NextLog)
{
m_NextLog = now + 10000;
PrintFormat("ERR CONVOY COVER: step=%1 held=%2 resumed=%3 report=%4 observed=%5 routeIndex=%6 waypoint=%7 reports=%8",
m_Step, m_Lead.Combat.Held, m_Resumed, m_Lead.Combat.ReportId, m_Lead.Combat.ObservedAt, m_Lead.RouteIndex, m_Lead.Waypoint != null, m_Reports.Count());
}
if (m_Step == 1 && m_Held)
{
if (m_Lead.Waypoint || m_Lead.RouteIndex != m_RouteIndex) { Check(false, "held native road order unexpectedly advanced"); return -1; }
if (now - m_HeldAt < 5000) return 0;
if (!Check(true, "owned road waypoint stayed absent and route index stable for five seconds")) return -1;
PrintFormat("ERR CONVOY COVER held native movement=%1m (native combat movement is not suppressed)", vector.DistanceXZ(m_HoldPosition, m_Lead.Entity.GetOrigin()));
MovePrisoner(m_HidePosition); m_Step = 2;
Print("ERR CONVOY COVER arrangement: same prisoner moved to distant authored extraction site; native visibility and radio must expire nearby evidence.");
}
if (m_Step == 2 && m_Resumed)
{
if (vector.DistanceXZ(m_ResumePosition, m_Lead.Entity.GetOrigin()) < 20) return 0;
if (!Check(!m_Lead.Combat.Held && now > m_ResumedAt && Unchanged(),
"same native lead vehicle drove twenty metres after delivered RESUME; forces and cargo unchanged")) return -1;
Stop(); return 1;
}
return 0;
}
void Stop()
{
if (m_Stopped) return;
m_Stopped = true;
if (m_Protected && m_Damage) m_Damage.EnableDamageHandling(m_DamageWasEnabled);
m_Protected = false;
if (s_Active == this) s_Active = null;
}
void ~ERR_NativeConvoyCoverProbe() { Stop(); }
}
#endif