"""Execute production formation geometry and tactical lifecycle decisions.""" import re from policy_harness import ROOT,body,run p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HunterTactics.c' geometry='\n'.join(body(p,x) for x in ['static float ManeuverBudget(', 'static bool ExtendApproach(', 'static bool AdvanceWithoutVisualCover(', 'static bool DismountRange(', 'static bool Fresh(', 'static vector Offset(', 'static bool Stage(', 'static bool Foot(']) geometry=geometry.replace('array occupied','Array occupied').replace('foreach (vector other : occupied)','for (Vec other : occupied)') geometry=re.sub(r'\bvector (?=\w)','Vec ',geometry).replace('vector.','Geometry::').replace('Vector(','Vec(').replace('Math.Clamp','std::clamp').replace('30 + metres / 2, 90, 240','30 + metres / 2, 90.f, 240.f') stubs=r''' #include #include #include #include #include using string=std::string; 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 n){return n==0?x:n==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};}float Length(){return std::hypot(x,z);}void Normalize(){float length=Length();x/=length;z/=length;}string ToString(){return "position";}}; namespace Geometry{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;}} templateusing Array=std::vector; ''' run(stubs+geometry+r''' int main(){ assert(DismountRange(140)&&DismountRange(450)&&!DismountRange(139)&&!DismountRange(451)); assert(ExtendApproach(1000,91000,91000)); assert(!ExtendApproach(1000,75999,91000)); assert(!ExtendApproach(1000,301000,301000)); assert(!ExtendApproach(1000,92000,91000)); assert(!ExtendApproach(92000,90000,91000)); assert(!AdvanceWithoutVisualCover(true,47000,16999)); assert(AdvanceWithoutVisualCover(true,47000,17000)); assert(!AdvanceWithoutVisualCover(false,47000,47000)); assert(ManeuverBudget(0)==90000&&ManeuverBudget(300)==180000&&ManeuverBudget(10000)==240000); assert(Fresh(0,60000)&&!Fresh(0,60001)&&!Fresh(100,99)&&!Fresh(-1,0)); Vec c{300,0,500}; for(int a=0;a<360;a+=15){Vec f{std::sin(a*3.14159265f/180),0,std::cos(a*3.14159265f/180)}; Vec cover=Offset(c,f,0,0),left=Offset(c,f,1,0),right=Offset(c,f,2,0); assert(Stage(cover,c,f,{}));assert(Stage(left,c,f,{cover}));assert(Stage(right,c,f,{cover,left})); assert(!Stage(left,c,f,{left}));assert(!Stage(c,c,f,{}));assert(!Stage(c+f*300,c,f,{})); for(int role=1;role<3;role++){assert(Foot(Offset(c,f,role,1),c,f,role));assert(Foot(Offset(c,f,role,2),c,f,role));assert(!Foot(Offset(c,f,3-role,2),c,f,role));} } } ''') tick='\n'.join(body(p,x) for x in ['protected bool RecoverApproach(', 'protected bool UpdateCover(', 'bool Tick(ERR_HQ hq, float now)']).replace('protected ', '').replace('bool assigned;', 'bool assigned=false;').replace('m_ApproachRetries.ToString()', 'std::to_string(m_ApproachRetries)').replace('assigned.ToString()', 'std::to_string(assigned)') for typ in ['ERR_HQ','IEntity','AIWaypoint']: tick=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',tick) for n in ['Radio','hq','Unit','Attack','search','coordination','m_GunTeam']: tick=tick.replace(n+'.',n+'->') tick=tick.replace('ERR_HQ.Get()', 'ERR_HQ::Get()').replace('vector reserved', 'Vec reserved').replace('AttackSpace().Claim', 'AttackSpace()->Claim') tick=tick.replace('foreach (IEntity* crew : Unit->Members)','for (IEntity* crew : Unit->Members)') tick=tick.replace('Unit->Entity.GetOrigin()','Unit->Entity->GetOrigin()') tick=tick.replace('ERR_HunterAttackRules.', '').replace('Math.Min', 'std::min').replace('now + 30000, m_ApproachStarted + 300000','now + 30000.f, m_ApproachStarted + 300000.f') tick=tick.replace('AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.SearchWaypoint(), Sector))','SpawnSearch(Sector)') tick=tick.replace('ERR_ConvoyMountedCrew.Hold','Hold').replace('ERR_HunterCombat.Active','Active').replace('vector.DistanceXZ','Geometry::DistanceXZ').replace('null','nullptr') run(stubs+body(p,'static bool DismountRange(')+body(p,'static bool ExtendApproach(')+body(p,'static bool AdvanceWithoutVisualCover(')+body(p,'static float ManeuverBudget(').replace('Math.Clamp','std::clamp').replace('30 + metres / 2, 90, 240','30 + metres / 2, 90.f, 240.f')+r''' struct IEntity{Vec pos;Vec GetOrigin(){return pos;}}; struct AIWaypoint{void SetCompletionRadius(float){}}; struct Space{void Claim(void*,Vec,float){}};struct ERR_HQ{void CoordinatedHunterDelivery(string,int,bool,float){}Space space;static ERR_HQ*Get(){return nullptr;}Space*AttackSpace(){return &space;}int Journal(){return 0;}}; struct RadioType{int MessageId=1;int result=0;int Advance(float,int){return result;}}; struct GroupType{bool active=false;};bool Active(GroupType*g){return g->active;} int holds=0;void Hold(IEntity*,IEntity*){holds++;} struct UnitType{string Source="hunter";struct GroupType*Group=nullptr;IEntity*Entity=nullptr;ArrayMembers;void*Downlink=nullptr;void CancelHQDownlink(){Downlink=nullptr;}bool CombatHeld=false;float CombatQuietUntil=0;int clears=0;void ClearOrder(){clears++;}}; struct AttackType{Vec ReportPosition;float Started=0;bool alive=true,ready=false;bool CoverAlive(){return alive;}bool CoverReady(){return ready;}}; struct GunTeam{int ticks=0;void Tick(float){ticks++;}void Pause(){}}; struct Tactics{ int m_ApproachRetries=0,m_CoverRetries=0,alternates=0;float m_BlindSince=-1;bool alternateWorks=true; bool AlternateStage(float now){alternates++;if(!alternateWorks)return false;State=1;m_BlindSince=-1;m_ApproachProgress=now;Stage={500,0,-250};return true;} GunTeam*m_GunTeam=nullptr;bool align=false,facing=false;bool TryAlign(){return align;}bool FacingSector(){return facing;} UnitType*Unit=nullptr;AttackType*Attack=nullptr;RadioType*Radio=nullptr;int Role=1,State=0,Retries=0;float m_TravelDistance=0,Deadline=0,NextRetry=0,m_ApproachStarted=0,m_ApproachProgress=0;Vec m_ApproachPosition;bool m_UnsupportedSearch=false,Delivered=false,Stopped=false,living=true,aboard=true,moveWorks=true,boardWorks=true,searchWorks=true;int clears=0,moves=0,boards=0;Vec Stage,Approach,Sector,position;AIWaypoint search; bool visual=false;bool VisualCover(){return visual;}bool Living(){return living;}void Stop(){Stopped=true;Radio=nullptr;Clear();if(Delivered)Unit->CombatHeld=false;}bool Move(Vec,float=20){moves++;return moveWorks;}void Clear(){clears++;}bool Board(bool){boards++;return boardWorks;}bool Aboard(bool required){return aboard==required;}Vec Position(){return position;}void VehicleUse(bool){}void Note(string){}void ObserveState(float){}void Add(AIWaypoint*){}AIWaypoint*SpawnSearch(Vec){return searchWorks?&search:nullptr;} '''+tick+r''' }; struct Fixture{IEntity car,crew;GroupType group;UnitType unit;AttackType attack;RadioType radio;Tactics t;ERR_HQ hq; Fixture(){car.pos={0,0,-250};unit.Entity=&car;unit.Group=&group;unit.Members={&crew};t.Unit=&unit;t.Attack=&attack;t.Radio=&radio;t.Stage=car.pos;t.Approach={-170,0,-85};t.Sector={-105,0,35};t.position=car.pos;} }; int main(){ {Fixture f;f.radio.result=1;f.t.Role=0;f.t.Tick(&f.hq,1000);assert(f.t.State==3); f.t.Tick(&f.hq,20999);assert(f.t.alternates==0);f.t.Tick(&f.hq,21000);assert(f.t.alternates==1&&f.t.State==1);} {Fixture f;f.radio.result=1;f.t.Role=0;f.t.visual=true;f.t.Tick(&f.hq,1000);f.t.Tick(&f.hq,120000);assert(f.t.State==3&&f.t.alternates==0);} {Fixture f;f.radio.result=1;f.t.Role=0;f.t.alternateWorks=false;f.t.Tick(&f.hq,1000); f.t.Tick(&f.hq,21000);f.t.Tick(&f.hq,22000);assert(f.t.alternates==1); f.t.Tick(&f.hq,41000);f.t.Tick(&f.hq,61000);assert(f.t.alternates==3&&!f.t.Stopped); assert(!f.t.Tick(&f.hq,81000)&&f.t.Stopped);} {Fixture f;f.radio.result=1;f.t.Role=0;f.t.alternateWorks=false;f.t.m_TravelDistance=1000;f.car.pos={0,0,-1100};f.t.Stage={0,0,-210}; f.t.Tick(&f.hq,1000);f.t.Tick(&f.hq,30999);assert(f.t.moves==1); f.t.Tick(&f.hq,31000);assert(f.t.moves==2&&f.t.m_ApproachRetries==1); f.t.Tick(&f.hq,61000);assert(f.t.alternates==1&&f.t.m_ApproachRetries==2); assert(!f.t.Tick(&f.hq,91000)&&f.t.Stopped);} {Fixture f;f.radio.result=1;f.t.Role=0;f.t.m_TravelDistance=1000;f.car.pos={0,0,-1100};f.t.Stage={0,0,-210}; f.t.Tick(&f.hq,1000);for(int i=1;i<=5;i++){f.car.pos.z+=20;f.t.Tick(&f.hq,1000+i*20000);}assert(f.t.m_ApproachRetries==0&&!f.t.Stopped);} {Fixture f;f.radio.result=1;f.t.Role=0;f.t.Stage={0,0,-500};f.t.visual=true; assert(f.t.Tick(&f.hq,1000)&&f.t.State==3); // sees fight at 250m: stop, don't back away to stage holds=0; } {Fixture f;assert(!f.t.Tick(&f.hq,1000));assert(!f.t.Delivered&&!f.unit.CombatHeld&&!f.unit.clears&&f.t.moves==0);f.radio.result=-1;assert(!f.t.Tick(&f.hq,2000)&&f.t.Stopped&&f.unit.clears==0);} {Fixture f;f.radio.result=1;f.t.moveWorks=false;assert(!f.t.Tick(&f.hq,1000)&&!f.t.Delivered&&f.unit.clears==0);} {Fixture f;f.radio.result=1;assert(f.t.Tick(&f.hq,1000));assert(f.t.State==2&&f.t.boards==1&&f.unit.clears==1&&f.unit.CombatHeld);f.t.aboard=false;assert(f.t.Tick(&f.hq,2000)&&f.t.State==3);int moves=f.t.moves;f.t.Tick(&f.hq,5000);assert(f.t.moves==moves);f.attack.ready=true;f.t.Tick(&f.hq,6000);assert(f.t.State==4&&f.t.moves==moves+1);f.t.Tick(&f.hq,7000);assert(f.t.moves==moves+1);f.t.position=f.t.Approach;f.t.Tick(&f.hq,8000);assert(f.t.State==5);f.t.Tick(&f.hq,68000);assert(f.t.State==6);f.t.aboard=true;assert(!f.t.Tick(&f.hq,69000)&&f.t.Stopped&&!f.unit.CombatHeld);} {Fixture f;f.radio.result=1;f.t.Tick(&f.hq,1000);f.t.aboard=false;f.t.Tick(&f.hq,2000);f.t.Tick(&f.hq,16999);assert(f.t.State==3);f.t.Tick(&f.hq,17000);assert(f.t.State==4&&f.t.moves==2);} {Fixture f;f.radio.result=1;f.t.Tick(&f.hq,1000);f.t.aboard=false;f.t.Tick(&f.hq,2000);f.attack.alive=false;f.t.Tick(&f.hq,47000);assert(f.t.State==4&&f.t.moves==2&&f.t.m_UnsupportedSearch);} {Fixture f;f.radio.result=1;f.car.pos=f.t.position={0,0,-1000};f.t.Stage={1000,0,1000};f.t.Tick(&f.hq,1000);assert(f.t.State==1);f.car.pos={0,0,-950};f.t.Tick(&f.hq,91000);assert(!f.t.Stopped&&f.t.Deadline==121000);f.t.Tick(&f.hq,121000);assert(f.t.Stopped);} {Fixture f;f.radio.result=1;f.car.pos=f.t.position={0,0,-1000};f.t.Stage={1000,0,1000};f.t.Tick(&f.hq,1000);f.car.pos={0,0,-950};f.t.Tick(&f.hq,301000);assert(f.t.Stopped);} {Fixture f;f.radio.result=1;f.t.Tick(&f.hq,1000);f.t.aboard=false;f.attack.ready=true;f.t.Tick(&f.hq,2000);assert(f.t.State==4);f.attack.alive=false;f.t.Tick(&f.hq,3000);assert(f.t.State==3);} {Fixture f;f.t.Role=0;f.radio.result=1;f.t.Tick(&f.hq,1000);assert(f.t.State==3&&f.t.boards==0&&holds==1);assert(!f.t.Tick(&f.hq,300000)&&f.t.Stopped);} {Fixture f;f.radio.result=1;f.t.Stage={1000,0,1000};f.t.Tick(&f.hq,1000);assert(f.t.State==2); } // Road passes within infantry range before exact stage. {Fixture f;f.radio.result=1;f.t.Tick(&f.hq,1000);f.t.aboard=false;f.t.Tick(&f.hq,2000);f.attack.alive=false;f.t.Tick(&f.hq,47000);assert(f.t.State==4&&f.t.m_UnsupportedSearch);f.t.Tick(&f.hq,48000);assert(f.t.State==4); } // No repeated cover-loss reset. {Fixture f;f.radio.result=1;f.t.Tick(&f.hq,1000);f.t.aboard=false;f.t.Tick(&f.hq,2000);f.attack.alive=false;f.t.moveWorks=false;f.t.Tick(&f.hq,47000);assert(f.t.State==6); } // Failed foot order still regroups. {Fixture f;f.radio.result=1;f.t.align=true;f.t.Tick(&f.hq,1000);assert(f.t.State==8&&f.t.boards==0);f.t.Tick(&f.hq,20999);assert(f.t.State==8);f.t.Tick(&f.hq,21000);assert(f.t.State==2&&f.t.boards==1);} // Alignment never blocks beyond 20s. {Fixture f;f.radio.result=1;f.t.align=true;f.t.Tick(&f.hq,1000);f.t.facing=true;f.t.Tick(&f.hq,2000);assert(f.t.State==2);} // Stop aligning when facing. {Fixture f;f.radio.result=1;f.t.align=true;f.t.Tick(&f.hq,1000);f.group.active=true;f.t.Tick(&f.hq,2000);assert(f.t.State==2);} // Combat preempts alignment. {Fixture f;f.radio.result=1;f.t.Tick(&f.hq,1000);f.t.living=false;assert(!f.t.Tick(&f.hq,2000)&&f.t.Stopped);} } ''') print('PASS: separated rear vehicle approaches, opposite infantry sectors, radio-gated ownership, failed-order preservation, visual cover or bounded supported reconnaissance, bounded dismount/search/regroup and loss cleanup') # Execute the ownership methods too: an interrupted flank must restore only its # own vehicle and dispose only its own waypoints, including allocation failures. own='\n'.join(body(p,x) for x in ['protected void Clear(', 'protected void Add(', 'protected void VehicleUse(', 'protected bool Board(', 'void Stop()']) own=own.replace('protected ','').replace('foreach (','for (') for typ in ['AIWaypoint','SCR_BoardingWaypoint','SCR_AIGroupUtilityComponent','SCR_AIVehicleUsageComponent','IEntity','ERR_HQ']: own=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',own) for name in ['Unit','utility','waypoint','m_Mode','coordination','pendingHQ','Radio','m_GunTeam']: own=own.replace(name+'.',name+'->') own=own.replace('ERR_HQ.Get()', 'ERR_HQ::Get()').replace('AttackSpace().Release', 'AttackSpace()->Release') own=own.replace('Unit->Group.','Unit->Group->').replace('Unit->Entity.FindComponent(SCR_AIVehicleUsageComponent)','Unit->Entity->usage') own=own.replace('SCR_AIVehicleUsageComponent.Cast','UsageCast').replace('SCR_BoardingWaypoint.Cast','BoardCast') for cls in ['ERR_ConvoyRules','ERR_SupplyConvoyTactics','ERR_ConvoyMountedCrew','SCR_EntityHelper']: own=own.replace(cls+'.',cls+'::') own=own.replace('Unit->Recovery.SuspendBoarding()', 'Unit->Recovery->SuspendBoarding()') own=own.replace('SCR_AIActionBase.PRIORITY_LEVEL_PLAYER','1000').replace('null','nullptr') run(r''' #include #include #include templatestruct Array:std::vector{void Insert(T v){this->push_back(v);}void Clear(){this->clear();}}; struct AIWaypoint{bool boarding=true;int deleted=0;}; struct SCR_BoardingWaypoint:AIWaypoint{int priority=0;bool driver=false,turret=false,passenger=false;void SetPriorityLevel(int n){priority=n;}void SetAllowance(bool a,bool b,bool c){driver=a;turret=b;passenger=c;}}; SCR_BoardingWaypoint*BoardCast(AIWaypoint*w){return w&&w->boarding?static_cast(w):nullptr;} struct SCR_AIVehicleUsageComponent{};auto UsageCast(SCR_AIVehicleUsageComponent*u){return u;} struct SCR_AIGroupUtilityComponent{int adds=0,removes=0;void AddUsableVehicle(SCR_AIVehicleUsageComponent*){adds++;}void RemoveUsableVehicle(SCR_AIVehicleUsageComponent*){removes++;}}; struct Group{SCR_AIGroupUtilityComponent utility;Arraywaypoints;auto GetGroupUtilityComponent(){return &utility;}void RemoveWaypoint(AIWaypoint*w){auto&i=waypoints;i.erase(std::remove(i.begin(),i.end(),w),i.end());}void AddWaypoint(AIWaypoint*w){waypoints.Insert(w);}}; struct IEntity{SCR_AIVehicleUsageComponent usageValue;SCR_AIVehicleUsageComponent*usage=&usageValue;}; struct Recovery{void SuspendBoarding(){}}; struct UnitType{const char*Source="hunter";struct Recovery*Recovery=nullptr;struct Group*Group=nullptr;IEntity*Entity=nullptr;ArrayMembers;bool CombatHeld=true;float NextOrder=99;}; struct Mode{bool active=true;bool Active(){return active;}}; namespace ERR_ConvoyRules{bool captured=false;bool Captured(IEntity*,Group*){return captured;}} namespace ERR_ConvoyMountedCrew{int released=0;void Release(IEntity*){released++;}} namespace SCR_EntityHelper{void DeleteEntityAndChildren(AIWaypoint*w){w->deleted++;}} namespace ERR_SupplyConvoyTactics{AIWaypoint*next=nullptr;bool requested=false;AIWaypoint*Board(IEntity*,bool enter){requested=enter;return next;}} struct World{float GetWorldTime(){return 0;}};struct Game{World GetWorld(){return {};}};Game GetGame(){return {};} struct RadioWire{int MessageId=1;}; struct Space{void Release(void*){}};struct ERR_HQ{void CoordinatedHunterDelivery(const char*,int,bool,float){}Space space;static ERR_HQ*Get(){return nullptr;}Space*AttackSpace(){return &space;}}; struct ERR_HunterGunTeam{inline static bool usable=false;int stopped=0;ERR_HunterGunTeam(UnitType*){}bool Start(){return usable;}void Stop(){stopped++;}}; struct Tactics{ERR_HunterGunTeam*m_GunTeam=nullptr;void Note(const char*){}UnitType*Unit=nullptr;Mode*m_Mode=nullptr;int Role=0;bool m_VehicleDetached=false,Stopped=false,Delivered=false,living=true;RadioWire*Radio=nullptr;ArrayOrders;bool Living(){return living&&!Stopped;} '''+own+r''' }; int main(){ Group g;IEntity car,man;UnitType u;u.Group=&g;u.Entity=&car;u.Members.Insert(&man);Mode mode;Tactics t;t.Unit=&u;t.m_Mode=&mode; AIWaypoint unrelated,old;g.AddWaypoint(&unrelated);t.Add(&old); assert(!t.Board(false)&&t.Orders.size()==1&&old.deleted==0&&g.waypoints.size()==2); AIWaypoint wrong;wrong.boarding=false;ERR_SupplyConvoyTactics::next=&wrong; assert(!t.Board(false)&&wrong.deleted==1&&old.deleted==0); SCR_BoardingWaypoint exit;ERR_SupplyConvoyTactics::next=&exit; assert(t.Board(false)&&!ERR_SupplyConvoyTactics::requested&&old.deleted==1); assert(exit.priority==1000&&exit.driver&&exit.turret&&exit.passenger&&unrelated.deleted==0); t.VehicleUse(false);t.VehicleUse(false);assert(g.utility.removes==1&&t.m_VehicleDetached); t.Delivered=true;t.Stop();assert(t.Stopped&&g.utility.adds==1&&exit.deleted==1&&unrelated.deleted==0&&g.waypoints.size()==1&&!u.CombatHeld&&u.NextOrder==0); t.Stop();assert(g.utility.adds==1&&exit.deleted==1&&ERR_ConvoyMountedCrew::released==1); ERR_HunterGunTeam::usable=true;Tactics split;split.Unit=&u;split.m_Mode=&mode;assert(split.Board(false)&&split.m_GunTeam);auto held=split.m_GunTeam; assert(split.Board(false)&&split.m_GunTeam==held);split.Stop();assert(held->stopped==1&&!split.m_GunTeam);split.Stop();assert(held->stopped==1);ERR_HunterGunTeam::usable=false; int added=g.utility.adds; for(int scenario=0;scenario<3;scenario++){ Tactics x;x.Unit=&u;x.m_Mode=&mode;x.m_VehicleDetached=true; ERR_ConvoyRules::captured=scenario==0;mode.active=scenario!=1;x.living=scenario!=2; x.Stop();assert(g.utility.adds==added); // Never reclaim a captured/dead/teardown vehicle. } } ''') print('PASS: production dismount/boarding allocation, all crew allowances, waypoint ownership, one-time vehicle detach/restore and capture/teardown guards') armed=(body(p,'static bool CoverCrew(').replace('ERR_HunterVehicle unit','UnitType* unit').replace('ERR_GameMode mode','int mode')+'\n'+body(p,'bool ArmedCover()')).replace('foreach (','for (').replace('BaseCompartmentSlot seat','BaseCompartmentSlot* seat').replace('bool gunner;','bool gunner=false;') for name in ['Unit','unit','seat','m_GunTeam']:armed=armed.replace(name+'.',name+'->') armed=armed.replace('ECompartmentType.TURRET','1').replace('ERR_Actors.CanFight','CanFight') armed=armed.replace('ERR_HQMountedAmmoSnapshot ammo','ERR_HQMountedAmmoSnapshot* ammo').replace('ERR_HQMountedAmmoSampler.Inspect','Inspect').replace('ammo.','ammo->') cover=body(p,'bool CoverAlive()').replace('foreach (','for (').replace('ERR_HunterTactics team','ERR_HunterTactics* team').replace('team.','team->') run(r''' #include #include struct Man{bool fit=true;};bool CanFight(Man*m){return m&&m->fit;} struct BaseCompartmentSlot{Man*occupant=nullptr;int type=1;int GetType(){return type;}Man*GetOccupant(){return occupant;}}; struct Members{Man*original;bool Contains(Man*m){return m==original;}}; struct ERR_HQMountedAmmoSnapshot{bool Complete=true;int Rounds=99,Chambers=1;}ammo;auto Inspect(int){return &ammo;} struct UnitType{int Entity=0;struct Members Members;std::vectorSeats;bool driver=true;bool CrewValid(int){return driver;}}; struct GunTeam{bool covered=true;bool Cover(){return covered;}}; struct ERR_HunterTactics{GunTeam*m_GunTeam=nullptr;UnitType*Unit;int Role=0,State=3,m_Mode=0;bool Delivered=true,living=true;bool Living(){return living;} '''+armed+r''' };struct Attack{std::vectorTeams; '''+cover+r''' };int main(){Man gunner,outsider;BaseCompartmentSlot slot;slot.occupant=&gunner;UnitType u;u.Members.original=&gunner;u.Seats={&slot};ERR_HunterTactics t;t.Unit=&u;Attack a;a.Teams={&t};assert(a.CoverAlive());gunner.fit=false;assert(!a.CoverAlive());gunner.fit=true;slot.occupant=&outsider;assert(!a.CoverAlive());slot.occupant=&gunner;u.driver=false;assert(!a.CoverAlive());u.driver=true;t.Delivered=false;assert(!a.CoverAlive());t.Delivered=true;t.living=false;assert(!a.CoverAlive());t.living=true;ammo.Rounds=0;ammo.Chambers=0;assert(!a.CoverAlive());ammo.Chambers=1;assert(a.CoverAlive());ammo.Complete=false;assert(!a.CoverAlive());GunTeam split;t.m_GunTeam=&split;t.Role=1;t.State=4;assert(a.CoverAlive());split.covered=false;assert(!a.CoverAlive());} ''') print('PASS: covering support requires the original conscious turret gunner, valid mounted driver, live crew and delivered assignment') # Execute production per-vehicle approach geometry: opposite arrivals stay on # their own sides instead of sharing the cover vehicle's staging axis. approach=body(p,'static vector Approach(').replace('vector ', 'Vec ') run(stubs+approach+r''' int main(){ Vec contact{100,0,100},fallback{0,0,1}; for(Vec origin : {Vec{100,0,-900},Vec{100,0,1100},Vec{-900,0,100},Vec{1100,0,100}}){ Vec forward=Approach(origin,contact,fallback); Vec stage=contact-forward*260; assert(Geometry::Dot(stage-contact,origin-contact)>0); assert(Geometry::DistanceXZ(origin,stage)', 'Array').replace('foreach (', 'for (').replace('vector ', 'Vec ').replace('ERR_HunterTactics other', 'Tactics* other') for name in ['Unit','Attack','other']: alternate=alternate.replace(name+'.',name+'->') alternate=alternate.replace('Entity.GetOrigin()', 'Entity->GetOrigin()').replace('Math.Min', 'std::min').replace('ERR_HunterAttackRules.ManeuverBudget', 'ManeuverBudget').replace('m_ApproachStarted + 300000', 'm_ApproachStarted + 300000.f') run(stubs.replace('templateusing Array=std::vector;', 'templatestruct Array:std::vector{using std::vector::vector;void Insert(T v){this->push_back(v);}};')+r''' struct Entity{Vec point{0,0,-600};Vec GetOrigin(){return point;}}; struct UnitType{struct Entity entity;struct Entity*Entity=&entity;}; struct Tactics;struct AttackType{ArrayTeams;}; float ManeuverBudget(float){return 240000;} struct Tactics{ UnitType unit;UnitType*Unit=&unit;AttackType attack;AttackType*Attack=&attack; Vec Stage{0,0,-210},Approach{155,0,-240},Sector{105,0,35},m_ApproachPosition; Arraym_RejectedStages,reservations;int State=3;float m_BlindSince=1000,m_ApproachProgress=0,m_ApproachStarted=1000,m_TravelDistance=1000,Deadline=100; bool live=true,plan=true,move=true;bool Living(){return live;} bool Plan(Array occupied){reservations=occupied;Stage={200,0,-210};Approach={200,0,-85};Sector={105,0,35};return plan;} bool Move(Vec,float){return move;}void Note(string){} '''+alternate+r''' }; int main(){ for(int failure=0;failure<2;failure++){ Tactics t;t.plan=failure!=0;t.move=failure!=1; assert(!t.AlternateStage(21000));assert(t.Stage.x==0&&t.Approach.x==155&&t.State==3&&t.m_RejectedStages.empty()); } Tactics t,other,dead;dead.live=false;other.Stage={-200,0,-210};t.attack.Teams={&t,&other,&dead};t.m_RejectedStages.Insert({-100,0,-300}); assert(t.AlternateStage(21000));assert(t.reservations.size()==4&&t.reservations.back().x==-200); assert(t.State==1&&t.Stage.x==200&&t.m_BlindSince==-1&&t.m_ApproachProgress==21000&&t.m_RejectedStages.size()==2); assert(t.Deadline==261000);assert(t.AlternateStage(200000)&&t.Deadline==301000); // retries cannot extend the total approach budget } ''') print('PASS: alternate positions exclude failed and occupied stages, roll back on failed planning/allocation, and preserve the total time budget')