Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
@@ -0,0 +1,365 @@
class ERR_AirPassenger
{
IEntity Actor;
BaseCompartmentSlot Seat;
bool Locked = true;
bool Jumped;
ERR_AssaultParachute Chute;
}
class ERR_AirAssaultTeam
{
SCR_AIGroup Group;
ref array<ref ERR_AirPassenger> Members = {};
ref ERR_AirDropOrder Order;
bool Released;
void Landed() {} // Canopy completion is observed by the owning load for the entire original team.
void Update()
{
if (Released || !Group || !Order || Group.GetPlayerCount(true) > 0) return;
bool survivor;
foreach (ERR_AirPassenger member : Members)
{
if (!ERR_Actors.Alive(member.Actor)) continue;
if (!member.Jumped || CompartmentAccessComponent.GetVehicleIn(member.Actor) || member.Actor.GetOrigin()[1] - SCR_TerrainHelper.GetTerrainY(member.Actor.GetOrigin()) > 3) return;
survivor = survivor || ERR_Actors.CanFight(member.Actor);
}
if (!survivor) return;
Released = true;
Group.ActivateAI();
Group.SetPermanentLOD(-1);
ERR_HQ.Get().LandAirTeam(Group, Order);
}
}
class ERR_AirDropGeometry
{
static string LastFailure;
static float Along(vector position, vector center, vector direction)
{ vector offset = position - center;
offset[1] = 0;
return vector.Dot(offset, direction);
}
static bool Corridor(vector position, vector center, vector direction, vector velocity)
{
float along = Along(position, center, direction);
vector lateral = position - center - direction * along;
lateral[1] = 0;
velocity[1] = 0;
return along >= -175 && along <= 250 && lateral.Length() <= 45 && velocity.Length() >= 15 && vector.Dot(velocity.Normalized(), direction) >= 0.8;
}
static bool LandingLine(vector center, vector direction)
{
LastFailure = "clear";
for (int step = -4; step <= 5; step++)
{
vector point = ERR_Actors.Ground(center + direction * (step * 50));
if (point[1] < 2 || ChimeraWorldUtils.TryGetWaterSurfaceSimple(GetGame().GetWorld(), point)) { LastFailure = "water/shore"; return false; }
if (!ERR_HQRoadIntercept.Standable(point)) { LastFailure = "landing ground not standable"; return false; }
TraceBox trace = new TraceBox();
trace.Start = point + "0 3 0";
trace.End = point + "0 190 0";
trace.Mins = "-4 -1 -4";
trace.Maxs = "4 1 4";
trace.Flags = TraceFlags.ENTS | TraceFlags.WORLD;
if (GetGame().GetWorld().TraceMove(trace, null) < 1) { LastFailure = "obstructed descent column"; return false; }
}
return true;
}
}
// Eight original passengers, two original teams, one consumed load per aircraft.
class ERR_AirAssaultLoad
{
static const ResourceName PARACHUTE = "{C31F210ABE0781A0}Prefabs/ERR/AssaultParachute.et";
protected ERR_GameMode m_Mode;
protected ERR_ReconFlightComponent m_Flight;
protected Vehicle m_Aircraft;
protected int m_Serial, m_NextPassenger;
protected ref array<BaseCompartmentSlot> m_Seats = {};
protected ref array<ref ERR_AirPassenger> m_Passengers = {};
protected ref array<ref ERR_AirAssaultTeam> m_Teams = {};
protected ref ERR_AirDropOrder m_Order;
protected float m_CoverUntil;
bool Cover(float now, out vector center) { if (!m_Order || !m_Used || m_Approaching || now >= m_CoverUntil) return false;
center = m_Order.Target;
return true;
}
bool NeedsService(float now) { return m_Used && !m_Approaching && now >= m_CoverUntil;
}
protected float m_LoadDeadline, m_NextBoard, m_NextJump, m_AirspaceUntil;
protected bool m_Ready, m_Failed, m_Stopped, m_Used, m_Approaching, m_RunCommitted;
protected vector m_Center, m_Direction, m_RunStart, m_RunEnd;
static void SetLock(IEntity actor, bool locked)
{
ERR_HQ hq = ERR_HQ.Get();
if (hq && hq.AirAssault()) hq.AirAssault().Lock(actor, locked);
}
void ReuseRemaining()
{
m_Used = false;
m_Order = null;
m_CoverUntil = 0;
foreach (ERR_AirPassenger member : m_Passengers)
if (member.Actor && CompartmentAccessComponent.GetVehicleIn(member.Actor) == m_Aircraft) { member.Jumped = false;
member.Locked = true;
}
}
int Count() { return m_Passengers.Count();
}
ERR_AirPassenger At(int index) { return m_Passengers[index];
}
bool Used() { return m_Used;
}
bool Preparing() { return !m_Ready && !m_Failed && !m_Stopped;
}
bool Ready()
{
return m_Ready && !m_Failed && !m_Stopped && !m_Used && m_Flight && !m_Flight.Lost()
&& (m_Flight.Phase() == ERR_ReconPhase.SEARCH || m_Flight.Phase() == ERR_ReconPhase.SHADOW) && Aboard() > 0;
}
void ERR_AirAssaultLoad(ERR_GameMode mode, ERR_ReconFlightComponent flight, int serial)
{ m_Mode = mode;
m_Flight = flight;
m_Aircraft = flight.Aircraft();
m_Serial = serial;
}
protected void Seats(IEntity entity)
{
BaseCompartmentManagerComponent manager = BaseCompartmentManagerComponent.Cast(entity.FindComponent(BaseCompartmentManagerComponent));
if (manager)
{
array<BaseCompartmentSlot> seats = {};
manager.GetCompartments(seats);
foreach (BaseCompartmentSlot seat : seats)
if (seat && seat.GetType() == ECompartmentType.CARGO && seat.IsCompartmentAccessible() && !seat.IsOccupied() && !m_Seats.Contains(seat)) m_Seats.Insert(seat);
}
IEntity child = entity.GetChildren();
while (child) { Seats(child);
child = child.GetSibling();
}
}
protected void Failure(string reason)
{
m_Failed = true;
m_Approaching = false;
foreach (ERR_AirPassenger member : m_Passengers) member.Locked = false;
ERR_HQ.Event("AIR LOAD UNAVAILABLE", reason + "; original partial entities retained, reconnaissance remains available");
}
void Load(vector pad)
{
m_LoadDeadline = GetGame().GetWorld().GetWorldTime() + 30000;
Seats(m_Aircraft);
ResourceName soldier = m_Mode.FactionSelection().Soldier();
Resource chute = Resource.Load(PARACHUTE);
if (m_Seats.Count() < 8 || soldier.IsEmpty() || !chute || !chute.IsValid()) { Failure("eight usable cargo seats, soldier or canopy unavailable");
return;
}
for (int t = 0; t < 2; t++)
{
ERR_AirAssaultTeam team = new ERR_AirAssaultTeam();
team.Group = SCR_AIGroup.Cast(ERR_Actors.Spawn("{C31F210ABE078195}Prefabs/ERR/ReconCrew.et", pad));
m_Teams.Insert(team);
if (!team.Group) { Failure("passenger group spawn failed");
return;
}
m_Mode.Own(team.Group);
team.Group.SetFaction(m_Mode.EnemyFaction());
team.Group.SetPermanentLOD(0);
}
for (int i = 0; i < 8; i++)
{
ERR_AirPassenger member = new ERR_AirPassenger();
member.Seat = m_Seats[i];
AIGroup group = m_Teams[i % 2].Group;
member.Actor = member.Seat.SpawnCharacterInCompartment(soldier, group, "{C31F210ABE078195}Prefabs/ERR/ReconCrew.et");
m_Passengers.Insert(member);
m_Teams[i % 2].Members.Insert(member);
if (!member.Actor || group != m_Teams[i % 2].Group) { Failure("native passenger creation or group identity failed");
return;
}
}
}
protected void Boarding(float now)
{
foreach (ERR_AirAssaultTeam loadingTeam : m_Teams)
{
if (!loadingTeam.Group) continue;
loadingTeam.Group.ActivateAI();
array<AIAgent> loadingAgents = {};
loadingTeam.Group.GetAgents(loadingAgents);
foreach (AIAgent loadingAgent : loadingAgents) { loadingAgent.SetPermanentLOD(0);
loadingAgent.ActivateAI();
}
}
if (now >= m_LoadDeadline) { Failure(string.Format("native passenger boarding timed out; seated=%1/%2", Aboard(), Count()));
return;
}
bool all = m_Passengers.Count() == 8;
foreach (ERR_AirPassenger member : m_Passengers)
{
if (!ERR_Actors.CanFight(member.Actor)) { all = false;
continue;
}
if (CompartmentAccessComponent.GetVehicleIn(member.Actor) == m_Aircraft && member.Seat.GetOccupant() == member.Actor) continue;
all = false;
if (now < m_NextBoard || CompartmentAccessComponent.GetVehicleIn(member.Actor) || member.Seat.IsOccupied()) continue;
SCR_CompartmentAccessComponent access = SCR_CompartmentAccessComponent.Cast(member.Actor.FindComponent(SCR_CompartmentAccessComponent));
if (access) access.MoveInVehicle(m_Aircraft, ECompartmentType.CARGO, false, member.Seat);
}
if (now >= m_NextBoard) m_NextBoard = now + 1000;
if (!all) return;
for (int t = 0; t < m_Teams.Count(); t++)
{
ERR_AirAssaultTeam team = m_Teams[t];
foreach (ERR_AirPassenger original : team.Members)
if (ERR_Actors.Group(original.Actor) != team.Group || !ERR_Actors.SetFaction(original.Actor, m_Mode.EnemyFaction())) { Failure("original passenger faction/group changed");
return;
}
ERR_ForceRecord record = m_Mode.Forces().Register(string.Format("airborne-%1-team-%2", m_Serial, t), ERR_ForceKind.PATROL, m_Aircraft.GetOrigin(), 4, null, team.Group, now);
if (!record) { Failure("finite passenger ledger refused registration");
return;
}
record.Pinned = true;
m_Mode.RegisterGroup(team.Group);
ERR_HQ.Get().ReserveGroup(team.Group);
team.Group.ActivateAI();
array<AIAgent> agents = {};
team.Group.GetAgents(agents);
foreach (AIAgent agent : agents) { agent.SetPermanentLOD(0);
agent.ActivateAI();
}
}
m_Ready = true;
ERR_HQ.Event("AIR LOAD READY", "eight original passengers seated in two four-person teams");
}
int Aboard()
{
if (!m_Aircraft) return 0;
int count;
foreach (ERR_AirPassenger member : m_Passengers)
if (ERR_Actors.CanFight(member.Actor) && !ERR_Actors.Player(member.Actor) && CompartmentAccessComponent.GetVehicleIn(member.Actor) == m_Aircraft) count++;
return count;
}
bool HoldsSeat(IEntity actor)
{
if (m_Stopped || m_Failed || !ERR_Actors.CanFight(actor) || ERR_Actors.Player(actor)) return false;
foreach (ERR_AirAssaultTeam team : m_Teams)
foreach (ERR_AirPassenger member : team.Members)
if (member.Actor == actor && member.Locked && ERR_Actors.Group(actor) == team.Group)
return (m_Flight && m_Flight.Phase() < ERR_ReconPhase.PARKED && CompartmentAccessComponent.GetVehicleIn(actor) == m_Aircraft) || (member.Chute && CompartmentAccessComponent.GetVehicleIn(actor) == member.Chute);
return false;
}
void Lock(IEntity actor, bool locked)
{ foreach (ERR_AirPassenger member : m_Passengers) if (member.Actor == actor) member.Locked = locked;
}
bool Begin(ERR_AirDropOrder order, float now)
{
if (!Ready() || !order || now >= order.ExpiresAt) return false;
vector radial = m_Aircraft.GetOrigin() - order.Target;
radial[1] = 0;
float heading = Math.Atan2(radial[0], radial[2]);
bool found;
int surveyed;
map<string, int> rejected = new map<string, int>();
// Several distances and both runway directions provide alternatives around
// woodland while retaining every landing/descent safety check.
for (int attempt = 0; attempt < 72; attempt++)
{
int bearing = attempt % 12;
int ring = Math.Floor(attempt / 24.0);
float radius = 300 + ring * 200;
float angle = heading + bearing * Math.PI / 6;
radial = Vector(Math.Sin(angle), 0, Math.Cos(angle));
m_Center = ERR_Actors.Ground(order.Target + radial * radius);
m_Direction = Vector(-radial[2], 0, radial[0]);
if ((attempt % 24) >= 12) m_Direction = radial;
surveyed++;
if (ERR_AirDropGeometry.LandingLine(m_Center, m_Direction)) { found = true; break; }
int count; rejected.Find(ERR_AirDropGeometry.LastFailure, count);
rejected.Set(ERR_AirDropGeometry.LastFailure, count + 1);
}
string summary;
foreach (string reason, int failures : rejected) summary += reason + "=" + failures.ToString() + "; ";
ERR_HQ.Event("AIR DROP SURVEY", string.Format("target=%1 checked=%2 accepted=%3 center=%4; %5", order.Target, surveyed, found, m_Center, summary));
if (!found) return false;
m_RunStart = m_Center - m_Direction * 350;
m_RunEnd = m_Center + m_Direction * 450;
m_CoverUntil = now + 300000;
m_Order = order;
m_Used = true;
m_Approaching = true;
m_RunCommitted = false;
m_NextPassenger = 0;
m_NextJump = now;
foreach (ERR_AirAssaultTeam team : m_Teams) team.Order = order;
m_AirspaceUntil = order.ExpiresAt + 150000;
return true;
}
bool Approach(out vector point)
{
point = m_RunStart;
if (m_RunCommitted) point = m_RunEnd;
return m_Approaching && !m_Stopped;
}
void Abort()
{ if (m_Approaching && m_Order) m_CoverUntil = GetGame().GetWorld().GetWorldTime() + 300000;
m_Approaching = false;
m_RunCommitted = false;
}
bool BlocksMortar(vector impact, float now)
{
if (!m_Order || vector.DistanceXZ(impact, m_Order.Target) >= 1200) return false;
foreach (ERR_AirPassenger member : m_Passengers)
if (member.Chute && member.Actor && CompartmentAccessComponent.GetVehicleIn(member.Actor) == member.Chute) return true;
return now < m_AirspaceUntil;
}
void Update(float now)
{
if (m_Stopped || !Replication.IsServer() || !m_Mode.Active()) return;
foreach (ERR_AirAssaultTeam team : m_Teams) team.Update();
if (Preparing()) { Boarding(now);
return;
}
if (!m_Approaching) return;
if (!m_Flight || m_Flight.Lost() || m_Flight.Phase() >= ERR_ReconPhase.RETURNING || !ERR_Actors.Alive(m_Aircraft) || now >= m_Order.ExpiresAt)
{ Abort();
ERR_HQ.Event("AIR DROP ABORTED", "authorization expired or aircraft unavailable; no new passengers created");
return;
}
foreach (ERR_AirAssaultTeam originalTeam : m_Teams)
if (!originalTeam.Group || originalTeam.Group.GetPlayerCount(true) > 0 || originalTeam.Group.GetFaction() != m_Mode.EnemyFaction()) { Abort();
return;
}
vector position = m_Aircraft.GetOrigin();
if (!m_RunCommitted && vector.DistanceXZ(position, m_RunStart) <= 80) m_RunCommitted = true;
if (m_RunCommitted && ERR_AirDropGeometry.Along(position, m_Center, m_Direction) > 300) { Abort();
return;
}
float height = position[1] - SCR_TerrainHelper.GetTerrainY(position);
if (!m_RunCommitted || now < m_NextJump || height < 90 || height > 180 || !m_Aircraft.GetPhysics()
|| !ERR_AirDropGeometry.Corridor(position, m_Center, m_Direction, m_Aircraft.GetPhysics().GetVelocity())) return;
while (m_NextPassenger < m_Passengers.Count())
{
int index = m_NextPassenger++;
ERR_AirPassenger passenger = m_Passengers[index];
if (passenger.Jumped || !ERR_Actors.CanFight(passenger.Actor) || ERR_Actors.Player(passenger.Actor) || CompartmentAccessComponent.GetVehicleIn(passenger.Actor) != m_Aircraft) continue;
vector exit = m_Aircraft.CoordToParent("12 -4 0");
ERR_AssaultParachute chute = ERR_AssaultParachute.Cast(ERR_Actors.Spawn(PARACHUTE, exit));
m_NextJump = now + 2000;
if (!chute) { ERR_HQ.Event("AIR DROP FAILED", "canopy creation refused; original passenger retained");
continue;
}
m_Mode.Own(chute);
passenger.Chute = chute;
vector landing = m_Center + m_Direction * ERR_AirDropGeometry.Along(position, m_Center, m_Direction);
passenger.Jumped = chute.BeginJump(passenger.Actor, m_Teams[index % 2], landing);
ERR_HQ.Event("AIR DROP JUMP", string.Format("original passenger=%1 transfer requested=%2", index, passenger.Jumped));
break;
}
if (m_NextPassenger >= m_Passengers.Count()) Abort();
}
void Stop()
{
m_Stopped = true;
Abort();
foreach (ERR_AirPassenger passenger : m_Passengers) passenger.Locked = false;
}
}
@@ -0,0 +1,85 @@
// Decisions consume detached HQ memory and delivered friendly check-ins only.
class ERR_AirSupportSituation
{
int BeliefId;
int Samples;
int Spotted;
int GroundFit;
int CommittedFit;
int ConfirmedLosses;
float Age;
float Confidence;
float Radius;
float Speed;
bool MotionKnown;
bool Infantry;
bool AircraftReady;
bool ArtilleryBusy;
}
class ERR_AirSupportPolicy
{
int Candidate;
float ConsideringSince = -1;
float NextDrop;
string Reason = "waiting for delivered intelligence";
static string Refusal(ERR_AirSupportSituation s, bool checkAirspace = true)
{
if (!s || !s.AircraftReady) return "no ready helicopter passenger load";
if (!ERR_HQMemoryRules.Finite(s.Age) || !ERR_HQMemoryRules.Finite(s.Confidence) || !ERR_HQMemoryRules.Finite(s.Radius) || !ERR_HQMemoryRules.Finite(s.Speed)) return "invalid memory sample";
if (s.BeliefId <= 0 || s.Samples < 2 || s.Age < 0 || s.Age > 90 || s.Confidence < 0.7 || s.Confidence > 1 || s.Radius < 0 || s.Radius > 160) return "memory is too uncertain; continue reconnaissance";
if (!s.Infantry || !s.MotionKnown || s.Speed < 0 || s.Speed > 6) return "target movement does not justify an infantry drop";
int needed = Math.Clamp(s.Spotted * 2, 4, 12);
if (s.CommittedFit >= needed) return "ground forces already committed in sufficient strength";
if (s.GroundFit + s.CommittedFit >= needed && s.ConfirmedLosses < 2) return "nearby ground forces can handle this contact";
if (checkAirspace && s.ArtilleryBusy) return "mortar order or shells occupy the approach area";
return "";
}
bool Consider(ERR_AirSupportSituation s, float now)
{
if (!ERR_HQMemoryRules.Finite(now) || now < 0) return false;
// Artillery temporarily closes the approach; it does not invalidate fresh
// contact history or the confirmed lack of ground support. Restarting the
// timer on every salvo could prevent a drop through every clear interval.
string refusal = Refusal(s, false);
if (!refusal.IsEmpty()) { Candidate = 0;
ConsideringSince = -1;
Reason = refusal;
return false;
}
if (now < NextDrop) { Reason = "reinforcement cooldown; retain remaining troops";
return false;
}
if (Candidate != s.BeliefId || ConsideringSince < 0) { Candidate = s.BeliefId;
ConsideringSince = now;
}
if (s.ArtilleryBusy) { Reason = Refusal(s);
return false;
}
float delay = 45000;
if (s.ConfirmedLosses >= 2) delay = 20000;
if (now - ConsideringSince < delay) { Reason = "confirming persistent contact and ground-force shortfall";
return false;
}
Reason = "persistent infantry contact exceeds available ground response";
return true;
}
void Committed(float now) { NextDrop = now + 480000;
Candidate = 0;
ConsideringSince = -1;
}
void Retry(float now) { NextDrop = Math.Max(NextDrop, now + 15000);
Candidate = 0;
ConsideringSince = -1;
}
}
// One immutable authorization; receiver revalidates memory and physical safety.
class ERR_AirDropOrder
{
int BeliefId;
int BeliefRevision;
int MessageId;
vector Target;
float ExpiresAt;
ref ERR_HQReport Report;
}
@@ -0,0 +1,77 @@
"""Execute production HQ reinforcement policy against confidence and force scenarios."""
from policy_harness import ROOT, run
import re
source=(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AirSupportPolicy.c').read_text().split('// One immutable')[0]
source=source.replace('class ', 'struct ').replace('\n}', '\n};').replace('ERR_AirSupportSituation s','ERR_AirSupportSituation* s').replace('s.', 's->').replace('ERR_HQMemoryRules.','ERR_HQMemoryRules::').replace('Math.','Math::')
source=source.replace('ERR_HQMemoryRules->', 'ERR_HQMemoryRules::')
source=re.sub(r'\b(bool|int|float) (\w+);',r'\1 \2{};',source)
run(r'''
#include <cassert>
#include <string>
#include <cmath>
#include <algorithm>
#include <limits>
struct string:std::string{using std::string::string;bool IsEmpty(){return empty();}};
struct Math {static int Clamp(int x,int a,int b){return std::clamp(x,a,b);}static float Max(float a,float b){return std::max(a,b);}};
struct ERR_HQMemoryRules{static bool Finite(float v){return std::isfinite(v);}};
'''+source+r'''
int main(){
ERR_AirSupportSituation s{};s.AircraftReady=true;s.BeliefId=1;s.Samples=3;s.Spotted=3;s.Age=5;s.Confidence=.9;s.Radius=20;s.MotionKnown=true;s.Infantry=true;s.Speed=1;
ERR_AirSupportPolicy p;assert(!p.Consider(&s,0));assert(!p.Consider(&s,44999));assert(p.Consider(&s,45000));
p.Committed(45000);assert(!p.Consider(&s,50000));assert(!p.Consider(&s,525000));assert(p.Consider(&s,570000));
s.CommittedFit=6;assert(!p.Consider(&s,600000));s.CommittedFit=0;s.GroundFit=6;assert(!p.Consider(&s,600000));
s.ConfirmedLosses=2;assert(!p.Consider(&s,600000));assert(p.Consider(&s,620000));
s.ArtilleryBusy=true;assert(!p.Consider(&s,630000));s.ArtilleryBusy=false;assert(p.Consider(&s,640000));assert(p.Consider(&s,660000));
s.BeliefId=2;assert(!p.Consider(&s,670000));assert(!p.Consider(&s,689999));assert(p.Consider(&s,690000));
auto good=s;
s.Age=91;assert(!p.Consider(&s,700000));s=good;s.Confidence=.69;assert(!p.Consider(&s,700000));
s=good;s.Radius=161;assert(!p.Consider(&s,700000));s=good;s.Samples=1;assert(!p.Consider(&s,700000));
s=good;s.Infantry=false;assert(!p.Consider(&s,700000));s=good;s.Speed=7;assert(!p.Consider(&s,700000));
s=good;s.MotionKnown=false;assert(!p.Consider(&s,700000));s=good;s.AircraftReady=false;assert(!p.Consider(&s,700000));
s=good;s.Age=std::numeric_limits<float>::quiet_NaN();assert(!p.Consider(&s,700000));
assert(!p.Consider(nullptr,700000));assert(!p.Consider(&good,std::numeric_limits<float>::infinity()));
// Artillery blocks authorization, not the history of fresh sightings and
// ground-force shortage. Repeated <45-second clear windows used to starve
// airborne support by restarting its confirmation timer on every salvo.
ERR_AirSupportPolicy blocked;auto contact=good;contact.BeliefId=9;contact.GroundFit=0;contact.ConfirmedLosses=0;
assert(!blocked.Consider(&contact,0));
contact.ArtilleryBusy=true;
assert(!blocked.Consider(&contact,10000));
assert(!blocked.Consider(&contact,46000));
contact.ArtilleryBusy=false;assert(blocked.Consider(&contact,47000));
// Stale intelligence and a changed target still invalidate confirmation.
contact.Age=91;assert(!blocked.Consider(&contact,48000));
contact.Age=5;assert(!blocked.Consider(&contact,49000));
contact.BeliefId=10;assert(!blocked.Consider(&contact,90000));
assert(!blocked.Consider(&contact,134999));assert(blocked.Consider(&contact,135000));
// Busy airspace must not preconfirm a new drop during the 8-minute cooldown.
blocked.Committed(135000);contact.ArtilleryBusy=true;
assert(!blocked.Consider(&contact,614999));
contact.ArtilleryBusy=false;assert(!blocked.Consider(&contact,615000));
assert(!blocked.Consider(&contact,659999));assert(blocked.Consider(&contact,660000));
}
''')
print('PASS: production airborne memory confidence, persistence, ground coverage, confirmed losses, artillery veto, cooldown and invalid-input rules')
from policy_harness import body
load=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AirAssaultLoad.c'
cover=body(load,'bool Cover(').replace('out vector center','int& center').replace('m_Order.Target','m_Order->Target')
service=body(load,'bool NeedsService(')
run(r'''
#include <cassert>
struct Order {int Target=730;};
struct Load {
Order* m_Order=nullptr;bool m_Used=false,m_Approaching=false;float m_CoverUntil=300000;
'''+cover+service+r'''
};
int main(){
Load load;Order order;int center=-1;
assert(!load.Cover(0,center)&&center==-1&&!load.NeedsService(0));
load.m_Order=&order;load.m_Used=true;load.m_Approaching=true;
assert(!load.Cover(1,center)&&!load.NeedsService(300001));
load.m_Approaching=false;assert(load.Cover(299999,center)&&center==730&&!load.NeedsService(299999));
center=-1;assert(!load.Cover(300000,center)&&center==-1&&load.NeedsService(300000));
}
''')
print('PASS: production cover retains the carried HQ target until its deadline; airport reload waits until the drop pass and cover have finished')