"""Run the production column controller against physical-position/native-order doubles.""" from policy_harness import ROOT, body, run import re,os from pathlib import Path p=Path(os.environ.get('ERR_SUPPLY_COLUMN_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_SupplyConvoyColumn.c')) def cpp(s): s=re.sub(r'//[^\n]*', '', s).replace('protected ', '').replace('out vector ', 'vector& ') s=s.replace('foreach (', 'for (').replace('null','nullptr') for typ in ('ERR_ConvoyVehicle','ERR_ConvoyColumnVehicle'): s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s) for obj in ('m_Task','vehicle','state','scan','promoted','ahead','m_State[i]','currentLeader','m_Task->Vehicles[previous]'): s=s.replace(obj+'.',obj+'->') for obj in ('Entity','Group','Waypoint','Route','Receipt','Combat'): s=s.replace(obj+'.',obj+'->') for cls in ('ERR_ConvoyColumnRules','ERR_ConvoyDriving','ERR_SupplyConvoyTactics','ERR_HQ','Math','vector','string'): s=s.replace(cls+'.',cls+'::') s=s.replace('array', 'Array').replace('m_State[continuing].', 'm_State[continuing]->').replace('m_State[detached].', 'm_State[detached]->') s=s.replace('bool rejoining;', 'bool rejoining=false;').replace('m_State[returning].','m_State[returning]->').replace('m_Task->Vehicles[returning].','m_Task->Vehicles[returning]->') s=s.replace('m_State[previous].','m_State[previous]->') s=s.replace('m_State[index].', 'm_State[index]->').replace('m_State[leader].', 'm_State[leader]->').replace('m_State[survivor].', 'm_State[survivor]->') s=s.replace('m_Task->Vehicles[index].', 'm_Task->Vehicles[index]->').replace('(index + 1).ToString()', 'std::to_string(index + 1)') s=s.replace('float largestGap; bool supplyHold; bool interceptLive;', 'float largestGap=0; bool supplyHold=false; bool interceptLive=false;') return s rules='\n'.join(cpp(body(p, x)) for x in ('static bool WaitForRear(', 'static bool TooClose(', 'static float Speed(', 'static bool StraightAhead(')) methods='\n'.join(cpp(body(p,x)) for x in ('protected float DistanceToTarget(', 'protected void Reorder(', 'void Reset(', 'protected void Hold(', 'protected bool Progress(', 'bool Tick(')) trail='\n'.join(cpp(body(p,x)) for x in ('void Record(', 'bool Target(')) run(r''' #include #include #include #include struct string:std::string{using std::string::string;string(std::string s):std::string(s){}templatestatic string Format(T...){return "event";}}; struct vector{float x=0,z=0;float& operator[](int i){return i==0?x:z;}vector operator+(vector b){return{x+b.x,z+b.z};}vector operator-(vector b){return{x-b.x,z-b.z};}vector operator*(float n){return{x*n,z*n};}static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}}; namespace Math{float Max(float a,float b){return a>b?a:b;}int Min(int a,int b){return astruct Array:std::vector{using std::vector::vector;int Count(){return this->size();}void Insert(T value){this->push_back(value);}bool IsEmpty(){return this->empty();}void Clear(){this->clear();}void Remove(int i){this->erase(this->begin()+i);}}; struct ERR_ConvoyTrail{ArrayPoints;'''+trail+r'''}; struct ERR_ConvoyColumnRules{'''+rules+r'''}; struct Entity{vector position;vector GetOrigin(){return position;}}; struct Waypoint:Entity{}; struct Group{int added=0;void AddWaypoint(Waypoint*){added++;}}; struct Route{Arraypoints={{0},{400}};int Count(){return points.Count();}vector At(int i){return points[i];}}; struct Combat{bool Held=false;}; struct ERR_ConvoyVehicle{ int Index=0,RouteIndex=0,clears=0;bool InterceptActive=false,Released=false,Arrived=false,SupplyHeld=false; struct Entity entity;struct Entity* Entity=&entity;struct Group group;struct Group* Group=&group; struct Combat combat;struct Combat* Combat=&combat;struct Waypoint* Waypoint=nullptr; void ClearOrder(){if(Waypoint)clears++;delete Waypoint;Waypoint=nullptr;} void Release(void*){Released=true;ClearOrder();} bool Order(struct Route* route,float){if(!Waypoint){Waypoint=new struct Waypoint();Waypoint->position=route->At(RouteIndex);Group->AddWaypoint(Waypoint);}return true;} ~ERR_ConvoyVehicle(){ClearOrder();} }; namespace ERR_ConvoyDriving{bool Restore(void*,ERR_ConvoyVehicle*){return false;}} namespace ERR_SupplyConvoyTactics{Waypoint* Move(vector p,float){auto w=new Waypoint();w->position=p;return w;}} struct Receipt{string SourceId="test";}; struct ERR_SupplyConvoyTask{ArrayVehicles;struct Route route;struct Route* Route=&route;int Legs=0;struct Receipt receipt;struct Receipt* Receipt=&receipt;}; struct ERR_ConvoyColumnVehicle{ERR_ConvoyTrail Trail;bool InterceptDetached=false,Holding=false;vector Progress;float LastProgress=-1,NextRetry=0,NextTarget=0,Speed=-1;int Ahead=-1;}; struct Column{ void* m_Mode=nullptr;ERR_SupplyConvoyTask* m_Task;Arraym_State; Arraym_Order={0,1,2};int m_Leader=-1,m_RouteIndex=0;bool m_RearWait=false;float m_NextLog=0,m_RearWaitAt=-1; void Speed(ERR_ConvoyVehicle*,ERR_ConvoyColumnVehicle* state,float speed){state->Speed=speed;} '''+methods+r'''}; int main(){ // No stop/arrival dead zone behind a parked leader, including an empty trail after reform. for(float gap:{75.1f,78.f,80.f,90.f}){ ERR_ConvoyVehicle lead,tail;ERR_ConvoyColumnVehicle ls,ts;ERR_SupplyConvoyTask task; lead.entity.position={400};tail.entity.position={400-gap};task.Vehicles={&lead,&tail}; Column column{nullptr,&task,{&ls,&ts}};column.m_Order={0,1};column.m_RouteIndex=1;column.Tick(0); assert(lead.Arrived&&!tail.Arrived&&tail.Waypoint&&tail.Waypoint->position.x==365); tail.entity.position={326};column.Tick(1000);assert(tail.Arrived&&!tail.Waypoint); } // Dense straight samples select one lookahead target, then retain it while driving. {ERR_ConvoyVehicle lead;ERR_ConvoyColumnVehicle state;ERR_SupplyConvoyTask task; task.Vehicles={&lead};task.route.points={{0},{20},{40},{60},{80},{100},{120},{140},{160},{180},{200}}; Column column{nullptr,&task,{&state}};column.m_Order={0};column.Tick(0); assert(lead.RouteIndex==5&&lead.Waypoint->position.x==100&&lead.group.added==1); lead.entity.position={20};column.Tick(1000);assert(lead.RouteIndex==5&&lead.group.added==1); lead.entity.position={60};column.Tick(2000);assert(lead.RouteIndex==5&&lead.group.added==1); lead.entity.position={70};column.Tick(3000);assert(lead.RouteIndex==8&&lead.Waypoint->position.x==160&&lead.group.added==2); lead.entity.position={150};column.Tick(4000);assert(!lead.Arrived&&lead.Waypoint->position.x==200); lead.entity.position={164};column.Tick(5000);assert(!lead.Arrived); lead.entity.position={165};column.Tick(6000);assert(lead.Arrived); } assert(ERR_ConvoyColumnRules::StraightAhead({0,0},{70,0},{150,0})); assert(!ERR_ConvoyColumnRules::StraightAhead({0,0},{81,0},{150,0})); assert(!ERR_ConvoyColumnRules::StraightAhead({0,0},{70,0},{70,80})); assert(!ERR_ConvoyColumnRules::StraightAhead({0,0},{70,0},{0,0})); assert(!ERR_ConvoyColumnRules::StraightAhead({0,0},{70,0},{70,0})); {ERR_ConvoyTrail trail;vector target;assert(!trail.Target(target));trail.Record({0,0});trail.Record({0,50});trail.Record({50,50});assert(trail.Target(target)&&target.x==15&&target.z==50); // follows the bend, never a diagonal chord trail.Record({500,500});assert(!trail.Target(target)&&trail.Points.Count()==1);for(int i=1;i<100;i++)trail.Record({500.f+i*10,500});assert(trail.Points.Count()==48);} {ERR_ConvoyVehicle a,b,c;ERR_ConvoyColumnVehicle sa,sb,sc;ERR_SupplyConvoyTask task;task.Vehicles={&a,&b,&c};Column col{nullptr,&task,{&sa,&sb,&sc}};a.entity.position={100};b.entity.position={-200};c.entity.position={-300};col.Tick(0);assert(col.m_RearWait&&!a.Waypoint&&sa.Speed==0);col.Tick(20000);assert(a.Waypoint&&sa.Speed==12);} {ERR_ConvoyVehicle a,b,c;b.Index=1;c.Index=2;a.InterceptActive=true; ERR_ConvoyColumnVehicle sa,sb,sc;ERR_SupplyConvoyTask task;task.Vehicles={&a,&b,&c};Column column{nullptr,&task,{&sa,&sb,&sc}}; column.m_RouteIndex=1;a.RouteIndex=0;a.entity.position={-20};b.RouteIndex=1;b.entity.position={100};c.RouteIndex=1;c.entity.position={40};assert(column.Tick(0));assert(column.m_Leader==1); a.InterceptActive=false;b.Released=true;assert(column.Tick(1000));assert(column.m_Leader==2&&c.RouteIndex==1&&a.RouteIndex==0&&column.m_RouteIndex==1);} {ERR_ConvoyVehicle a,b,c;b.Index=1;c.Index=2;a.InterceptActive=true;c.Released=true; ERR_ConvoyColumnVehicle sa,sb,sc;ERR_SupplyConvoyTask task;task.Vehicles={&a,&b,&c};Column column{nullptr,&task,{&sa,&sb,&sc}}; column.m_RouteIndex=1;a.RouteIndex=0;a.entity.position={-20};b.RouteIndex=1;b.entity.position={100};assert(column.Tick(0));assert(column.m_Leader==1&&b.RouteIndex==1); a.InterceptActive=false;assert(column.Tick(1000));assert(column.m_Leader==1&&b.RouteIndex==1&&a.RouteIndex==0&&column.m_RouteIndex==1&&b.Waypoint->position.x==400);} {ERR_ConvoyVehicle a,b,c;b.Index=1;c.Index=2;a.InterceptActive=true;b.Released=c.Released=true; ERR_ConvoyColumnVehicle sa,sb,sc;ERR_SupplyConvoyTask task;task.Vehicles={&a,&b,&c};Column column{nullptr,&task,{&sa,&sb,&sc}}; a.RouteIndex=0;a.entity.position={-200};assert(column.Tick(0));assert(sa.InterceptDetached&&a.RouteIndex==0&&!a.Waypoint); column.m_RouteIndex=1;a.InterceptActive=false;assert(column.Tick(1000));assert(a.RouteIndex==0&&a.Waypoint&&column.m_RouteIndex==0);} // Two detached original escorts rejoin without changing the continuing leader's route. for(int returnFirst=0;returnFirst<3;returnFirst++){ ERR_ConvoyVehicle a,b,c;b.Index=1;c.Index=2;a.InterceptActive=c.InterceptActive=true; ERR_ConvoyColumnVehicle sa,sb,sc;ERR_SupplyConvoyTask task;task.Vehicles={&a,&b,&c};Column column{nullptr,&task,{&sa,&sb,&sc}}; column.m_RouteIndex=1;a.entity.position={-20};b.entity.position={100};c.entity.position={-80}; assert(column.Tick(0)&&column.m_Leader==1&&sa.InterceptDetached&&sc.InterceptDetached); assert(!a.Waypoint&&!c.Waypoint&&b.RouteIndex==1); if(returnFirst==0)a.InterceptActive=false; if(returnFirst==1)c.InterceptActive=false; if(returnFirst==2)a.InterceptActive=c.InterceptActive=false; assert(column.Tick(1000)&&column.m_Leader==1&&column.m_RouteIndex==1&&b.RouteIndex==1); a.InterceptActive=c.InterceptActive=false; assert(column.Tick(2000)&&column.m_Leader==1&&column.m_RouteIndex==1&&!sa.InterceptDetached&&!sc.InterceptDetached); assert(a.RouteIndex==0&&c.RouteIndex==0); // no artificial route advancement on detour return } // A returning escort becomes a continuing follower before the column leader is lost. {ERR_ConvoyVehicle a,b,c;b.Index=1;c.Index=2;a.InterceptActive=c.InterceptActive=true; ERR_ConvoyColumnVehicle sa,sb,sc;ERR_SupplyConvoyTask task;task.Vehicles={&a,&b,&c};Column column{nullptr,&task,{&sa,&sb,&sc}}; column.m_RouteIndex=1;a.entity.position={-20};b.entity.position={100};c.entity.position={40}; assert(column.Tick(0));c.InterceptActive=false;assert(column.Tick(1000)); b.Release(nullptr);a.InterceptActive=false;assert(column.Tick(2000)); assert(column.m_Leader==2&&column.m_RouteIndex==1&&c.RouteIndex==1&&a.RouteIndex==0); } // With no continuing vehicle, a sole returning original uses its own saved route. {ERR_ConvoyVehicle a,b,c;b.Index=1;c.Index=2;a.InterceptActive=c.InterceptActive=true;b.Released=true; ERR_ConvoyColumnVehicle sa,sb,sc;ERR_SupplyConvoyTask task;task.Vehicles={&a,&b,&c};Column column{nullptr,&task,{&sa,&sb,&sc}}; column.m_RouteIndex=1;a.entity.position={-200};c.entity.position={-260}; assert(column.Tick(0)&&column.m_Leader==-1&&!a.Waypoint&&!c.Waypoint); c.Release(nullptr);a.InterceptActive=false;assert(column.Tick(1000)); assert(column.m_Leader==0&&column.m_RouteIndex==0&&a.RouteIndex==0&&a.Waypoint); } assert(!ERR_ConvoyColumnRules::WaitForRear(160,false));assert(ERR_ConvoyColumnRules::WaitForRear(161,false)); assert(ERR_ConvoyColumnRules::WaitForRear(91,true));assert(!ERR_ConvoyColumnRules::WaitForRear(90,true)); assert(ERR_ConvoyColumnRules::TooClose(39,false));assert(!ERR_ConvoyColumnRules::TooClose(40,false)); assert(ERR_ConvoyColumnRules::TooClose(64,true));assert(!ERR_ConvoyColumnRules::TooClose(65,true)); ERR_ConvoyVehicle a,b,c;b.Index=1;c.Index=2;b.entity.position={-35};c.entity.position={-70}; ERR_ConvoyColumnVehicle sa,sb,sc;ERR_SupplyConvoyTask task;task.Vehicles={&a,&b,&c};Column column{nullptr,&task,{&sa,&sb,&sc}}; assert(column.Tick(0));assert(a.RouteIndex==1&&a.Waypoint&&a.Waypoint->position.x==400);assert(!b.Waypoint&&!c.Waypoint); a.entity.position={80};column.Tick(1000);assert(b.Waypoint&&b.Waypoint->position.x==45&&sb.Speed==30);assert(!c.Waypoint); int additions=b.group.added;a.entity.position={110};column.Tick(2000);assert(b.group.added==additions); // stable target, no per-second churn a.entity.position={200};column.Tick(3000);assert(column.m_RearWait&&!a.Waypoint&&b.Waypoint); // leader waits for laggard b.entity.position={130};c.entity.position={60};column.Tick(11000);assert(!column.m_RearWait&&a.Waypoint); assert(b.Waypoint->position.x==165); // native target follows predecessor, never remote base marker b.SupplyHeld=true;auto supplyOrder=b.Waypoint;column.Tick(12000);assert(!a.Waypoint&&b.Waypoint==supplyOrder&&sb.LastProgress==-1); b.SupplyHeld=false;b.combat.Held=true;column.Tick(13000);assert(b.Waypoint==supplyOrder&&sb.LastProgress==-1);b.combat.Held=false; // Real vehicle positions determine arrival; no stale seated-group origin is consulted. a.entity.position={400};b.entity.position={330};c.entity.position={260};column.Tick(20000); assert(a.Arrived&&b.Arrived&&c.Arrived&&!a.Waypoint&&!b.Waypoint&&!c.Waypoint); // Loss promotes the next surviving vehicle at the current route cursor, no replacement/restart at spawn. column.Reset(20500);assert(column.m_Leader==0&&column.m_RouteIndex==2); // combat reset preserves route progress for a later promotion a.Release(nullptr);b.Arrived=c.Arrived=false;column.Tick(21000);assert(column.m_Leader==1&&b.RouteIndex==1&&!b.Arrived&&b.Waypoint->position.x==400); // Commanded stationary time alone retries and eventually releases; holds reset this clock. ERR_ConvoyVehicle stuck;ERR_ConvoyColumnVehicle ss; assert(column.Progress(&stuck,&ss,0));assert(column.Progress(&stuck,&ss,45000));assert(ERR_HQ::retries==1&&!stuck.Released); column.Hold(&stuck,&ss,170000);assert(column.Progress(&stuck,&ss,180000));assert(!stuck.Released); stuck.entity.position={6};assert(column.Progress(&stuck,&ss,300000));assert(ss.LastProgress==300000); assert(column.Progress(&stuck,&ss,479999));assert(!column.Progress(&stuck,&ss,480000)&&stuck.Released&&ERR_HQ::stranded==1); column.Reset(500000,true);assert(sa.Trail.Points.IsEmpty()&&sb.Trail.Points.IsEmpty()&&sc.Trail.Points.IsEmpty());assert(sa.LastProgress==-1&&sb.LastProgress==-1&&!column.m_RearWait&&column.m_Leader==-1); // Post-combat physical order avoids sending the front truck toward a rear vehicle coming toward it. b.entity.position={100};c.entity.position={300};b.Arrived=c.Arrived=false;column.m_RouteIndex=1;column.Reset(500001,false,true); assert(column.m_Order[0]==2&&column.m_Order[1]==1&&column.m_Order[2]==0); // released vehicle last assert(task.Vehicles[1]==&b&&task.Vehicles[2]==&c); // combat roles and canonical identities unchanged column.Tick(500002);assert(column.m_Leader==2&&!c.Waypoint&&!b.Waypoint); // fresh formation never follows an old leg trail b.entity.position={230};column.Tick(500003);assert(c.Waypoint&&c.Waypoint->position.x==400); b.Release(nullptr);c.Release(nullptr);assert(!column.Tick(510000)); } ''') print('PASS: production convoy spacing, physical arrival, stable follower targets, supply ownership, leader promotion and bounded stuck recovery') q=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_SupplyConvoyTactics.c' orders=body(q,'protected void Orders(').replace('protected ','').replace('m_Task.','m_Task->').replace('vector.','vector::') run(r''' #include #include struct vector{float x=0;static float DistanceXZ(vector a,vector b){return std::abs(a.x-b.x);}}; struct Items{int count;int Count(){return count;}}; struct Task{Items Vehicles{6},Teams{2};}; struct Controller{Task task;Task* m_Task=&task;float m_NextOrders=0;vector m_LastOrderContact,m_Contact;bool m_OrdersIssued=false,m_TruckWithdrawn=false;int support=0,flank=0; void Support(int,bool,bool=false){support++;}void Flank(int,bool){flank++;} '''+orders+r'''}; int main(){ Controller c;c.Orders(true,0);assert(c.support==6&&c.flank==2); c.Orders(false,30000);assert(c.support==12&&c.flank==2&&c.m_NextOrders==60000); c.m_Contact={39};c.Orders(false,60000);assert(c.support==18&&c.flank==2); c.m_Contact={40};c.Orders(false,90000);assert(c.support==24&&c.flank==4); c.m_OrdersIssued=false;c.Orders(true,100000);assert(c.support==30&&c.flank==6); // new engagement can issue at same location } ''') print('PASS: convoy combat keeps native manoeuvres for unchanged contacts and refreshes when contact moves') probe=ROOT/'addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSupplyConvoyProbe.c' departure=body(probe,'protected int Departure(').replace('protected ','').replace('m_CombatTask.','m_CombatTask->').replace('Vehicles[1].','Vehicles[1]->').replace('Entity.','Entity->').replace('vector.','vector::') run(r''' #include #include struct vector{float x=0;static float DistanceXZ(vector a,vector b){return std::abs(a.x-b.x);}}; void Print(const char*){} struct Entity{vector position;vector GetOrigin(){return position;}}; struct Vehicle{bool Released=false;struct Entity entity;struct Entity* Entity=&entity;}; struct Bases{int Count(){return 2;}}; struct Task{bool Stopped=false;int Legs=0;Vehicle* Vehicles[3];struct Bases Bases;}; struct Probe{Task* m_CombatTask;int m_Phase=3,m_TestLegs=0;vector m_RouteResumePosition;bool m_AllMoved=true; '''+departure+r'''}; int main(){ Task task;Vehicle truck;task.Vehicles[1]=&truck;Probe p{&task};truck.entity.position={200}; assert(p.Departure()==0&&p.m_Phase==3); // earlier regroup displacement cannot pass task.Legs=1;assert(p.Departure()==0&&p.m_Phase==4&&p.m_RouteResumePosition.x==200); assert(p.Departure()==0);truck.entity.position={219};assert(p.Departure()==0); truck.entity.position={220};assert(p.Departure()==1&&p.m_Phase==5); truck.Released=true;assert(p.Departure()==-1); } ''') smoke=(ROOT/'addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSmoke.c').read_text() convoy=smoke.split('else if (m_ERRSmokeStep == 106)')[1].split('else if (m_ERRSmokeStep == 105)')[0] assert 'Transition(ERR_Phase.DEFEAT)' in convoy and 'm_ERRSmokeStep = 1061' in convoy assert 'damage.Kill' not in convoy and 'm_ERRSmokeStep = 9' not in convoy print('PASS: native convoy proof excludes pre-departure movement and ends via explicit fixture reset, not prohibited player death') boarding=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_ConvoyBoardingProgress.c' retry=body(boarding,'bool Retry(') refresh=body(q,'protected void RefreshBoarding(').replace('protected ','').replace('foreach (','for (').replace('m_Task.','m_Task->').replace('team.','team->').replace('Vehicles[2].','Vehicles[2]->').replace('BoardingProgress.','BoardingProgress->').replace('Receipt.','Receipt->').replace('ERR_HQ.','ERR_HQ::').replace('Math.','Math::').replace('string.','string::').replace('ERR_SupplyTeam team','ERR_SupplyTeam* team') run(r''' #include #include #include struct string{templatestatic string Format(T...){return {};}}; namespace Math{float Round(float x){return std::round(x);}} namespace ERR_HQ{void Event(const char*,string){}} struct ERR_ConvoyBoardingProgress{int Seated=-1,Retries=0;float Distance=-1,LastProgress=0,NextRetry=0; '''+retry+r'''}; struct ERR_SupplyTeam{bool Released=false,aboard=false;float BoardingRetryAt=0,distance=200;int seated=0,clears=0,adds=0;ERR_ConvoyBoardingProgress* BoardingProgress=nullptr; bool Aboard(void*,bool){return aboard;}int BoardingCount(void*,float& d){d=distance;return seated;}void Clear(){clears++;}void Add(int){adds++;}}; struct Vehicle{void* Entity=nullptr;};struct Receipt{const char* SourceId="test";}; struct Task{std::vectorTeams;Vehicle* Vehicles[3];struct Receipt receipt;struct Receipt* Receipt=&receipt;}; struct Tactics{Task* m_Task;int Board(void*,bool){return 1;} '''+refresh+r'''}; int main(){ ERR_ConvoyBoardingProgress p;assert(!p.Retry(0,200,0)); assert(!p.Retry(0,194,29000));assert(!p.Retry(0,194,58000));assert(p.Retry(0,194,59000)); assert(!p.Retry(1,100,60000));assert(!p.Retry(1,100,89999));assert(p.Retry(1,100,90000)); assert(p.Retry(1,100,120000));assert(!p.Retry(1,100,150000)&&p.Retries==3); // bounded even if no progress Task task;Vehicle truck;task.Vehicles[2]=&truck;ERR_SupplyTeam team,finished,released;finished.aboard=true;released.Released=true; task.Teams={&team,&finished,&released};Tactics t{&task};t.RefreshBoarding(0);assert(team.BoardingProgress&&!finished.BoardingProgress&&!released.BoardingProgress); t.RefreshBoarding(30000);assert(team.clears==1&&team.adds==0&&team.BoardingRetryAt==31000); t.RefreshBoarding(30999);assert(team.adds==0);t.RefreshBoarding(31000);assert(team.adds==1&&team.BoardingRetryAt==0); team.seated=6;team.distance=10;t.RefreshBoarding(40000);assert(team.clears==1);t.RefreshBoarding(69999);assert(team.clears==1); t.RefreshBoarding(70000);assert(team.clears==2);team.aboard=true;t.RefreshBoarding(71000);assert(team.adds==1); // do not disturb completed boarding delete team.BoardingProgress; } ''') print('PASS: boarding retries only after approach/seat progress stops, capped at three, with delayed native reissue and completed-team preservation') safe='\n'.join(body(q,s) for s in ('protected float InfantryDistance(', 'protected void ProtectInfantry(')) safe=safe.replace('protected ','').replace('foreach (','for (') safe=safe.replace('AICarMovementComponent movement = AICarMovementComponent.Cast(vehicle.Entity.FindComponent(AICarMovementComponent));', 'AICarMovementComponent* movement = &vehicle->Entity->movement;') for typ in ('IEntity','ERR_SupplyTeam','ERR_ConvoyVehicle'):safe=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',safe) for obj in ('m_Task','team','member','vehicle','movement'):safe=safe.replace(obj+'.',obj+'->') for cls in ('ERR_Actors','CompartmentAccessComponent','Math','vector'):safe=safe.replace(cls+'.',cls+'::') run(r''' #include #include #include struct vector{float x=0;static float DistanceXZ(vector a,vector b){return std::abs(a.x-b.x);}}; namespace Math{float Min(float a,float b){return afit;}} namespace CompartmentAccessComponent{IEntity* GetVehicleIn(IEntity* m){return m->aboard?m:nullptr;}} struct ERR_SupplyTeam{bool Released=false;std::vectorMembers;}; struct ERR_ConvoyVehicle{bool Released=false;IEntity* Entity;}; struct Task{std::vectorTeams;std::vectorVehicles;}; struct Tactics{Task* m_Task; '''+safe+r'''}; int main(){ IEntity truck{{0}},car{{100}},foot{{30}},passenger{{0}};passenger.aboard=true;ERR_SupplyTeam team{false,{&foot,&passenger}}; ERR_ConvoyVehicle a{false,&truck},b{false,&car};Task task{{&team},{&a,&b}};Tactics t{&task}; assert(t.InfantryDistance(&truck)==30);t.ProtectInfantry();assert(truck.movement.speed==8&&car.movement.speed==35); foot.aboard=true;t.ProtectInfantry();assert(truck.movement.speed==35&&car.movement.speed==35); foot.aboard=false;foot.fit=false;assert(t.InfantryDistance(&truck)>100000);foot.fit=true;team.Released=true;assert(t.InfantryDistance(&truck)>100000); team.Released=false;a.Released=true;truck.movement.speed=99;t.ProtectInfantry();assert(truck.movement.speed==99); // never retake a released vehicle } ''') print('PASS: slow only owned vehicles near fit dismounted infantry; ignore passengers, casualties and released teams/vehicles') restore=body(p,'static bool Restore(').replace('ERR_GameMode mode','void* mode').replace('ERR_ConvoyVehicle vehicle','ERR_ConvoyVehicle* vehicle') restore=restore.replace('SCR_AIVehicleUsageComponent usage = SCR_AIVehicleUsageComponent.Cast(vehicle.Entity.FindComponent(SCR_AIVehicleUsageComponent));','SCR_AIVehicleUsageComponent* usage = vehicle->Entity->usage;') restore=restore.replace('SCR_AIGroupUtilityComponent utility', 'SCR_AIGroupUtilityComponent* utility') for obj in ('vehicle','Group','utility','usage'):restore=restore.replace(obj+'.',obj+'->') restore=restore.replace('ERR_ConvoyMountedCrew.','ERR_ConvoyMountedCrew::') run(r''' #include struct SCR_AIVehicleUsageComponent{bool pilot=true;bool CanBePiloted(){return pilot;}}; struct Entity{SCR_AIVehicleUsageComponent* usage=nullptr;}; struct SCR_AIGroupUtilityComponent{void* m_VehicleMgr=this;bool known=false;int added=0;bool IsUsableVehicle(Entity*){return known;}void AddUsableVehicle(SCR_AIVehicleUsageComponent*){known=true;added++;}}; struct Group{SCR_AIGroupUtilityComponent* utility=nullptr;SCR_AIGroupUtilityComponent* GetGroupUtilityComponent(){return utility;}}; struct ERR_ConvoyVehicle{bool Released=false,valid=true;struct Group group;struct Group* Group=&group;struct Entity entity;struct Entity* Entity=&entity;void* Driver=this;bool CrewValid(void*){return valid;}}; namespace ERR_ConvoyMountedCrew{bool healthyOwned=true;bool Retain(void*,Entity*){return healthyOwned;}} '''+restore+r''' int main(){ int mode;ERR_ConvoyVehicle car;SCR_AIGroupUtilityComponent utility;SCR_AIVehicleUsageComponent usage;car.group.utility=&utility;car.entity.usage=&usage; assert(!Restore(nullptr,&car)&&!Restore(&mode,nullptr));car.Released=true;assert(!Restore(&mode,&car));car.Released=false; car.valid=false;assert(!Restore(&mode,&car));car.valid=true;ERR_ConvoyMountedCrew::healthyOwned=false;assert(!Restore(&mode,&car));ERR_ConvoyMountedCrew::healthyOwned=true; car.group.utility=nullptr;assert(!Restore(&mode,&car));car.group.utility=&utility;utility.m_VehicleMgr=nullptr;assert(!Restore(&mode,&car));utility.m_VehicleMgr=&utility; car.entity.usage=nullptr;assert(!Restore(&mode,&car));car.entity.usage=&usage;usage.pilot=false;assert(!Restore(&mode,&car));usage.pilot=true; assert(Restore(&mode,&car)&&utility.known&&utility.added==1);assert(!Restore(&mode,&car)&&utility.added==1); } ''') print('PASS: restore missing native driving registration only for healthy owned seated crews; preserve existing association and refuse invalid/released vehicles') # Execute the production support decision, with native perception/navigation # boundaries controlled: useful guns hold, masked guns reposition, moving # drivers keep their order, and failure never claims working cover. support=body(q,'protected void Support(').replace('protected ', '') for typ in ('ERR_SupplyTeam','ERR_ConvoyVehicle','AIWaypoint'): support=support.replace(typ+' ',typ+'* ') for name in ('orders','vehicle','movement','m_Task'): support=support.replace(name+'.',name+'->') support=support.replace('m_Task->Receipt.', 'm_Task->Receipt->').replace('Entity.GetOrigin()', 'Entity->GetOrigin()').replace('Orders[0].', 'Orders[0]->') support=support.replace('vector ', 'Vec ').replace('Vector(', 'Vec(').replace('vector.DistanceXZ', 'Distance') support=support.replace('ERR_MountedCover.', 'ERR_MountedCover::').replace('GetGame().GetWorld().GetWorldTime()', 'clockNow') support=support.replace('ERR_HQ.Event', 'Event').replace('string.Format', 'Format').replace('index.ToString()', 'std::to_string(index)') support=support.replace('float rear = 180, side;', 'float rear = 180, side=0;').replace('advance += left * 70', 'advance = advance + left * 70').replace('advance -= left * 70', 'advance = advance - left * 70') run(r''' #include #include #include #include using string=std::string;float clockNow=1000; struct Vec{float x=0,y=0,z=0;Vec(){}Vec(float a,float b,float c):x(a),y(b),z(c){}float operator[](int i){return i==0?x:i==1?y:z;}Vec operator-(Vec b){return{x-b.x,y-b.y,z-b.z};}Vec operator+(Vec b){return{x+b.x,y+b.y,z+b.z};}Vec operator*(float n){return{x*n,y*n,z*n};}string ToString(){return "point";}}; float Distance(Vec a,Vec b){return std::hypot(a.x-b.x,a.z-b.z);} templatestruct Array:std::vector{using std::vector::vector;bool IsEmpty(){return this->empty();}}; templatestring Format(string,T...){return "event";}void Event(string,string){} struct AIWaypoint{Vec point;Vec GetOrigin(){return point;}}; struct Entity{Vec point{0,0,-250};Vec GetOrigin(){return point;}}; struct ERR_ConvoyVehicle{struct Entity entity;struct Entity* Entity=&entity;void* Group=nullptr;}; struct ERR_SupplyTeam{float SupportProgressAt=-1;Vec SupportProgressPosition;int SupportSearchStep=0;ArrayOrders;int clears=0;void Clear(){Orders.clear();clears++;}void Add(AIWaypoint*p){Orders.push_back(p);}}; struct Receipt{string SourceId="convoy";};struct Task{struct Receipt receipt;struct Receipt* Receipt=&receipt;ArrayVehicles;}; namespace ERR_MountedCover{bool sees=false;bool SeesSector(void*,Vec){return sees;}Vec Approach(Vec,Vec,Vec){return{0,0,1};}} struct Tactics{Task task;Task*m_Task=&task;Arraym_Support;Vec m_Contact,m_Forward{0,0,1};bool usable=true,position=true,spawn=true;int plans=0,moves=0,claims=0;AIWaypoint waypoint; bool fallback=false;Array attempted;bool Usable(int){return usable;}bool SupportPosition(int,Vec desired,Vec&out,bool lane=true){plans++;attempted.push_back(desired);out=desired;return lane?position:fallback;}AIWaypoint*Move(Vec p){moves++;waypoint.point=p;return spawn?&waypoint:nullptr;}void Command(AIWaypoint*){}void Claim(void*,Vec,float){claims++;} '''+support+r''' }; struct Case{Tactics t;ERR_ConvoyVehicle car;ERR_SupplyTeam orders;AIWaypoint old; Case(){clockNow=1000;t.task.Vehicles=Array(6,&car);t.m_Support=Array(6,&orders);old.point={0,0,-500};orders.Orders={&old};ERR_MountedCover::sees=false;} }; int main(){ {Case c;c.t.Support(4,false,true);clockNow=30999;c.t.Support(4,false,true);assert(c.t.plans==0); clockNow=31000;c.t.Support(4,false,true);assert(c.t.moves==1&&c.t.plans==1);} {Case c;c.t.Support(4,false,true);clockNow=21000;c.car.entity.point.x+=20;c.t.Support(4,false,true); clockNow=41000;c.car.entity.point.x+=20;c.t.Support(4,false,true);assert(c.t.plans==0&&c.t.moves==0);} {Case c;c.t.position=false;c.t.Support(4,false);c.t.Support(4,false);c.t.Support(4,false); assert(c.t.attempted.size()==3&&c.t.attempted[0].x!=c.t.attempted[1].x&&c.t.attempted[1].x!=c.t.attempted[2].x);} {Case c;c.car.entity.point={0,0,-650};c.t.position=false;c.t.fallback=true;c.t.Support(4,false); assert(c.t.plans==2&&c.t.moves==1&&c.t.waypoint.point.z==-500);} {Case c;c.car.entity.point={0,0,-650};c.t.position=false;c.t.Support(4,false);assert(c.t.plans==2&&c.t.moves==0);} {Case c;ERR_MountedCover::sees=true;c.t.Support(4,false);assert(c.orders.Orders.empty()&&c.t.plans==0&&c.t.moves==0&&c.t.claims==1);} {Case c;c.t.Support(4,false,true);assert(c.orders.Orders[0]==&c.old&&c.t.plans==0&&c.t.moves==0);} // driver in transit {Case c;c.old.point=c.car.entity.point;c.t.Support(4,false,true);assert(c.t.plans==1&&c.t.moves==1&&c.orders.Orders[0]==&c.t.waypoint);} // masked arrived gun retries {Case c;c.t.position=false;c.t.Support(4,false);assert(c.orders.Orders.empty()&&c.t.moves==0&&c.t.claims==1);} // no unsafe invented position {Case c;c.t.spawn=false;c.t.Support(4,false);assert(c.orders.Orders[0]==&c.old);} // failed allocation preserves old command {Case c;ERR_MountedCover::sees=true;c.t.Support(1,false);assert(c.t.plans==1&&c.t.moves==1);} // ammunition truck still withdraws {Case c;c.t.usable=false;c.t.Support(4,false);assert(c.orders.Orders.empty()&&c.t.plans==0);} } ''') print('PASS: actual convoy support preserves visual cover and ongoing approach, retries masked arrived positions, withholds unsafe moves and preserves ammo withdrawal') # Exercise the actual geometry boundary for both firing positions and the # deliberately non-firing intermediate approach used by distant escorts. position=body(q,'protected bool SupportPosition(').replace('protected ','').replace('out vector ', 'Vec& ').replace('vector ', 'Vec ').replace('Vector(', 'Vec(') for typ in ('ERR_ConvoyVehicle','SCR_AIGroupUtilityComponent'): position=position.replace(typ+' ',typ+'* ') position=position.replace('ERR_ConvoyPlacement placement = new ERR_ConvoyPlacement();','ERR_ConvoyPlacement placement;') for name in ('m_Task','vehicle','utility'): position=position.replace(name+'.',name+'->') position=position.replace('Group.GetGroupUtilityComponent()', 'Group->GetGroupUtilityComponent()').replace('Entity.GetOrigin()', 'Entity->GetOrigin()') for cls in ('ERR_MountedCover','ERR_SupplyConvoySystem','ERR_Actors','ERR_ForceRules','Math','vector'): position=position.replace(cls+'.',cls+'::') position=position.replace('candidate += Vec(', 'candidate = candidate + Vec(') run(r''' #include #include #include struct Vec{float x=0,y=0,z=0;Vec(){}Vec(float a,float b,float c):x(a),y(b),z(c){}Vec operator-(Vec b){return{x-b.x,y-b.y,z-b.z};}Vec operator+(Vec b){return{x+b.x,y+b.y,z+b.z};}}; namespace vector{float DistanceXZ(Vec a,Vec b){return std::hypot(a.x-b.x,a.z-b.z);}float Dot(Vec a,Vec b){return a.x*b.x+a.y*b.y+a.z*b.z;}} struct SCR_AIGroupUtilityComponent{bool allowed=true;bool IsPositionAllowed(Vec){return allowed;}}; struct Group{SCR_AIGroupUtilityComponent utility;auto GetGroupUtilityComponent(){return &utility;}}; struct Entity{Vec pos{0,0,-650};Vec GetOrigin(){return pos;}}; struct ERR_ConvoyVehicle{struct Entity entity;struct Entity*Entity=&entity;struct Group group;struct Group*Group=&group;}; struct Task{std::vectorVehicles;}; namespace ERR_MountedCover{bool lane=true;Vec Approach(Vec,Vec,Vec){return{0,0,1};}bool ClearLane(Entity*,Vec,Vec){return lane;}} namespace ERR_SupplyConvoySystem{bool road=false;bool Approach(Vec p,Vec&out,float){out=p;return road;}} namespace ERR_Actors{Vec Ground(Vec p){return p;}} namespace ERR_ForceRules{float Heading(Vec,Vec,int){return 0;}} namespace Math{float RandomFloat(float,float){return 0;}} struct ERR_ConvoyPlacement{inline static bool clear=true;bool Clear(Vec,float,float){return clear;}}; struct Tactics{Task task;Task*m_Task=&task;Vec m_Contact,m_Forward;bool free=true;bool FreePosition(Group*,Vec){return free;} '''+position+r''' }; int main(){ Tactics t;ERR_ConvoyVehicle car;t.task.Vehicles=std::vector(6,&car);Vec out; for(bool road : {false,true}){ ERR_SupplyConvoySystem::road=road;ERR_MountedCover::lane=false; assert(!t.SupportPosition(4,{0,0,-500},out)); // masked point is not firing cover assert(t.SupportPosition(4,{0,0,-500},out,false)); // explicit intermediate approach assert(!t.SupportPosition(4,{0,0,-650},out,false)); // cannot select the current blind hold assert(!t.SupportPosition(4,{0,0,-90},out,false)); // cannot rush through the reported sector t.free=false;assert(!t.SupportPosition(4,{0,0,-500},out,false));t.free=true; car.group.utility.allowed=false;assert(!t.SupportPosition(4,{0,0,-500},out,false));car.group.utility.allowed=true; ERR_MountedCover::lane=true;assert(t.SupportPosition(4,{0,0,-500},out)); } ERR_SupplyConvoySystem::road=false;ERR_ConvoyPlacement::clear=false;assert(!t.SupportPosition(4,{0,0,-500},out,false)); assert(!t.SupportPosition(1,{0,0,-500},out)); // trucks still require roads } ''') print('PASS: production support geometry rejects occupied, disallowed, blocked and too-close positions; only explicit intermediate approaches waive the firing-lane requirement')