Enable finite automatic convoy startup and scoped gunner skill

This commit is contained in:
2026-09-20 08:23:19 +02:00
parent 782c163c68
commit 5bd66d76ca
44 changed files with 3694 additions and 48 deletions
@@ -87,6 +87,8 @@ class ERR_NativeHQMemoryProbe
protected float m_Deadline;
protected bool m_Failed;
protected bool m_ValueChecked;
protected bool m_AutoChecked;
protected float m_AutoReadyAt = -1;
protected int m_RouteRetries;
static bool Enabled()
@@ -167,14 +169,66 @@ class ERR_NativeHQMemoryProbe
}
return removed > 0;
}
protected int AutomaticConvoy(ERR_HQ hq)
{
if (m_AutoChecked) return 1;
if (!hq.ConvoyStartup() || !hq.ConvoyStartup().Enabled())
{ Check(false, "automatic convoy fixture requires enabled round snapshot"); return -1; }
ERR_ConvoyTask task = hq.ConvoyTask();
if (!task)
{
if (hq.ConvoyStartup().Finished()) { Check(false, "automatic convoy refused: " + hq.ConvoyStatus()); return -1; }
return 0;
}
if (task.Stopped) { Check(false, "automatic convoy stopped: " + task.Reason); return -1; }
if (!task.Ready) return 0;
float now = GetGame().GetWorld().GetWorldTime();
if (m_AutoReadyAt < 0) m_AutoReadyAt = now;
if (now - m_AutoReadyAt < 6000) return 0;
if (!Check(hq.ConvoyStartup().Status() == "canonical convoy dispatched" && task.Receipt && task.Receipt.Consumed
&& task.Vehicles.Count() == 6 && hq.Convoys().Count() == 1, "automatic startup owns one canonical six-vehicle task")) return -1;
int crew;
int gunners;
foreach (ERR_ConvoyVehicle vehicle : task.Vehicles)
{
if (!Check(vehicle.Registered && !vehicle.Released && vehicle.CrewValid(m_Mode)
&& vehicle.Members.Count() == ERR_ConvoyPool.CrewCount(vehicle.Index), "automatic convoy exact original seated crew " + vehicle.Index.ToString())) return -1;
crew += vehicle.Members.Count();
for (int member = 0; member < vehicle.Members.Count(); member++)
{
IEntity actor = vehicle.Members[member];
SCR_AICombatComponent combat = SCR_AICombatComponent.Cast(actor.FindComponent(SCR_AICombatComponent));
if (!Check(combat && combat.GetAISkill() >= EAISkill.EXPERT, "original automatic crew has scoped expert floor")) return -1;
BaseCompartmentSlot slot = vehicle.Seats[member];
if (slot && slot.GetType() == ECompartmentType.TURRET)
{
if (!Check(ERR_EnemyGunnerSkill.OriginalGunner(m_Mode, vehicle.Group, actor) && combat.GetAISkill() >= EAISkill.CYLON,
"original native turret gunner has scoped skill100")) return -1;
gunners++;
}
}
}
if (!Check(gunners > 0, "automatic convoy includes confirmed native turret gunners")) return -1;
if (!Check(crew == 19 && task.Receipt.OutstandingBodies == 0 && task.Receipt.OutstandingGroups == 0
&& hq.ConvoyPool().CommittedBodies() <= 60, "automatic nineteen-body convoy respects shared physical budget")) return -1;
if (!Check(!hq.StartScenarioConvoy() && hq.ConvoyTask() == task && hq.Convoys().Count() == 1,
"manual request after automatic startup cannot duplicate the convoy")) return -1;
m_AutoChecked = true; return 1;
}
int Tick(ERR_HQ hq, float now)
{
if (!Enabled() || !hq || !m_Mode || m_Failed) return -1;
if (m_Deadline == 0) m_Deadline = now + 120000;
if (m_Deadline == 0) m_Deadline = now + 150000;
if (now > m_Deadline) { Check(false, "fixture deadline"); return -1; }
if (m_Step == 0)
{
if (!hq.ERR_ProbeSquadsReady()) return 0;
bool automaticWasChecked = m_AutoChecked;
int automatic = AutomaticConvoy(hq);
if (automatic <= 0) return automatic;
// Startup admission and native crew readiness have their own bounded wait.
// Start the original memory/radio deadline once, after automatic proof.
if (!automaticWasChecked) m_Deadline = now + 120000;
if (!m_ValueChecked)
{
if (!Check(hq.ERR_MemoryFixtureRegisteredForces() == 6,