"""Execute production supply-circuit and combat timing policies with native boundaries doubled.""" from policy_harness import ROOT, body, run import re D=ROOT/'addons/core/Scripts/Game/ERR/10_AI' def cpp(s): s=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('override ','').replace('foreach (','for (').replace('null','nullptr') s=s.replace('m_Task.', 'm_Task->').replace('m_Mode.', 'm_Mode->').replace('m_Service.', 'm_Service->') for typ in ('ERR_SupplyConvoyTask','ERR_ConvoyVehicle','ERR_SupplyTeam','ERR_ForceRoute','IEntity'): s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s) for obj in ('task','vehicle','departure','team','lost','boarding','stranded','leg','infantryTeam','member','delayed'): s=s.replace(obj+'.',obj+'->') for cls in ('Math','ERR_SupplyConvoyRules','ERR_HQ','ERR_ForceRules','ERR_Actors','vector','string'): s=s.replace(cls+'.',cls+'::') s=s.replace('bool surviving, pending;','bool surviving=false, pending=false;') s=s.replace('float nearest = 400; bool found;', 'float nearest = 400; bool found=false;') s=s.replace('m_Support[released].', 'm_Support[released]->').replace('Vehicles[released].', 'Vehicles[released]->').replace('infantryTeam->Group.', 'infantryTeam->Group->') s=s.replace('->Receipt.', '->Receipt->').replace('Vehicles[2].', 'Vehicles[2]->') s=s.replace('.Entity.', '.Entity->').replace('->Entity.', '->Entity->') return s common=r''' #include #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;static float Distance(vector a,vector b){return std::abs(a.x-b.x);}static float DistanceXZ(vector a,vector b){return Distance(a,b);}}; templatestruct Array:std::vector{using std::vector::vector;int Count(){return this->size();}}; namespace Math{int RandomInt(int low,int){return low;}float RandomFloat(float low,float){return low;}} namespace ERR_HQ{void Event(string,string){}} namespace ERR_ForceRules{bool Finite(float v){return std::isfinite(v);}bool FiniteVector(vector v){return Finite(v.x);}} ''' next_base=cpp(body(D/'ERR_SupplyConvoySystem.c','static int NextBase(')) run(common+'struct ERR_SupplyConvoyRules{'+next_base+r'''}; struct Entity{vector position;vector GetOrigin(){return position;}}; struct ERR_ForceRoute{Array points;void Add(vector p){points.push_back(p);}void Seal(){}}; struct Receipt{string SourceId="test";}; struct ERR_ConvoyVehicle{bool Released=false,Arrived=false;int cleared=0,RouteIndex=3;struct Entity entity;struct Entity* Entity=&entity; void ClearOrder(){cleared++;}void Tick(void*,ERR_ForceRoute*,float){}}; struct ERR_SupplyConvoyTask{Array Vehicles;float TravelDeadline=1000,Departure=0;bool Waiting=false,stopped=false;int LastBase=-1,Legs=0; Array Bases={{0},{1000},{2000}};ERR_ForceRoute* Route=nullptr;struct Receipt receipt;struct Receipt* Receipt=&receipt; void Stop(void*,string){stopped=true;}}; struct Service{void* m_Mode=nullptr; '''+cpp(body(D/'ERR_SupplyConvoyService.c','protected void Circuit('))+r'''}; int main(){ for(int last=0;last<5;last++)for(int roll=0;roll<4;roll++){int next=ERR_SupplyConvoyRules::NextBase(5,last,roll);assert(next>=0&&next<5&&next!=last);} assert(ERR_SupplyConvoyRules::NextBase(1,0,0)==-1);assert(ERR_SupplyConvoyRules::NextBase(3,3,0)==-1); Service s;ERR_SupplyConvoyTask t;ERR_ConvoyVehicle a,b;t.Vehicles={&a,&b}; s.Circuit(&t,999);assert(!t.Waiting&&!a.Arrived);s.Circuit(&t,1000);assert(a.Arrived&&b.Arrived&&t.Waiting&&t.Departure==46000); s.Circuit(&t,45999);assert(t.Legs==0);s.Circuit(&t,46000);assert(t.Legs==1&&!t.Waiting&&!a.Arrived&&!b.Arrived&&t.LastBase==0&&t.TravelDeadline==646000); a.entity.position={250};b.entity.position={251};s.Circuit(&t,47000);assert(a.Arrived&&!b.Arrived&&!t.Waiting); b.entity.position={249};s.Circuit(&t,48000);assert(t.Waiting);s.Circuit(&t,93000);assert(t.LastBase==1&&t.Legs==2); a.Released=true;b.Released=true;s.Circuit(&t,94000);assert(t.stopped); // no replacements } ''') run(common+r''' struct Group{int GetFaction(){return 1;}};struct IEntity{};struct Mode{int EnemyFaction(){return 1;}}; namespace ERR_Actors{bool Alive(IEntity*){return true;}struct Group* Group(IEntity*){return nullptr;}} struct ERR_ConvoyVehicle{bool Released=false;void* Group=nullptr;void* Entity=nullptr;}; struct ERR_SupplyTeam{bool Released=false,aboard=true,living=true;struct Group nativeGroup;struct Group* Group=&nativeGroup;Array Members;int orders=0;void Clear(){} bool Aboard(void*,bool required){return aboard==required;}bool Living(){return living;}void Release(void*){Released=true;}void Add(int){orders++;}}; struct Receipt{string SourceId="test";}; struct ERR_SupplyConvoyTask{Array Vehicles;Array Teams;struct Receipt receipt;struct Receipt* Receipt=&receipt;}; struct Tactics{ERR_SupplyConvoyTask* m_Task;Mode mode;Mode* m_Mode=&mode;Arraym_Support;bool m_Stopped=false,m_TruckWithdrawn=false,seeing=false,truck=true; int m_State=0,starts=0,reorders=0,withdraws=0,finishes=0,m_DismountAttempts=0,flanks=0;float m_NextDismount=0;void Flank(int,bool){flanks++;}float m_LastContact=-1,m_NextOrders=0,m_BoardDeadline=0;vector m_Contact; bool Observe(void*,vector& p,float&){p={100};return seeing;}void Start(vector p,float){m_Contact=p;m_State=1;m_TruckWithdrawn=false;starts++;} void Orders(bool,float){reorders++;}bool InfantryAboard(bool required){for(auto t:m_Task->Teams)if(!t->Released&&!t->Aboard(nullptr,required))return false;return true;} void Support(int i,bool hold){if(i==2&&!hold)withdraws++;}bool Usable(int){return truck;}void Clear(){}int Board(void*,bool){return 1;} void Finish(float){m_State=0;finishes++;} '''+cpp(body(D/'ERR_SupplyConvoyTactics.c','void Report('))+cpp(body(D/'ERR_SupplyConvoyTactics.c','bool Tick('))+r'''}; int main(){ ERR_SupplyConvoyTask task;ERR_ConvoyVehicle driver;task.Vehicles={&driver,&driver,&driver};ERR_SupplyTeam left,right;task.Teams={&left,&right};Tactics t{&task}; t.Report({100},std::numeric_limits::quiet_NaN(),0);assert(t.starts==0); t.Report({100},-1,0);t.Report({100},10,0);t.Report({100},0,60001);assert(t.starts==0); t.Report({100},1000,1000);assert(t.starts==1&&t.m_State==1);t.Report({0},999,1001);assert(t.m_LastContact==1000); assert(t.Tick(60999)&&t.m_State==1&&t.withdraws==0);left.aboard=right.aboard=false; assert(t.Tick(61000)&&t.m_State==2&&t.withdraws==1&&left.orders==1&&right.orders==1&&t.m_BoardDeadline==181000); assert(t.Tick(180999));left.aboard=true;assert(!t.Tick(181000)&&t.finishes==1&&!left.Released&&right.Released); right.Released=false;t.Report({100},200000,200000);t.Tick(260000);assert(t.m_State==2); t.seeing=true;t.Tick(260001);assert(t.m_State==1&&t.starts==3); // fresh contact interrupts reboarding t.seeing=false;t.truck=false;t.Tick(320001);assert(t.m_State==0&&left.Released&&right.Released); // truck lost: release surviving infantry t.m_Stopped=true;t.Report({100},400000,400000);assert(t.starts==3&&!t.Tick(400000)); left.Released=right.Released=false;left.aboard=right.aboard=true;Tactics retry{&task};retry.Report({100},0,0); for(int i=1;i<=9;i++){retry.Report({100},10000*i,10000*i);retry.Tick(10000*i);} assert(retry.m_DismountAttempts==6&&retry.flanks==12); // six attempts per still-mounted team, no infinite retry } ''') startup=cpp(body(D/'ERR_SupplyConvoySystem.c','void Tick(')) run(common+r''' struct Service{int count=0;int Count(){return count;}}; struct Startup{Service* m_Service;bool m_Stopped=false,m_Automatic=true,create=false;int m_Target=3,m_Attempts=0,calls=0; float m_Began=-1,m_Next=0;string m_Reason; bool StartOne(){calls++;if(create)m_Service->count++;return create;} '''+startup+r'''}; int main(){ Service service;Startup s{&service};s.Tick(1000,true);s.Tick(30999,true);assert(!s.calls);s.Tick(31000,false);assert(!s.calls);s.Tick(36000,true);assert(s.calls==1); s.create=true;s.Tick(41000,true);s.Tick(46000,true);s.Tick(51000,true);assert(service.count==3&&s.calls==4);s.Tick(56000,true);assert(s.calls==4); Service noRoad;Startup fail{&noRoad};fail.Tick(0,true);for(int i=0;i<200;i++)fail.Tick(30000+5000*i,true);assert(fail.calls==120&&fail.m_Attempts==120); Startup off{&noRoad};off.m_Target=0;off.Tick(0,true);off.Tick(30000,true);assert(!off.calls); off.m_Target=3;off.m_Automatic=false;off.Tick(90000,true);assert(!off.calls); } ''') retain=body(D/'ERR_SupplyConvoyTactics.c','static bool Retain(') retain=retain.replace('IEntity actor','IEntity* actor').replace('IEntity vehicle','IEntity* vehicle') retain=retain.replace('SCR_VehicleDamageManagerComponent damage = SCR_VehicleDamageManagerComponent.Cast(vehicle.FindComponent(SCR_VehicleDamageManagerComponent));','SCR_VehicleDamageManagerComponent* damage = vehicle->damage;') for name in ('ERR_Actors','CompartmentAccessComponent','EDamageState'):retain=retain.replace(name+'.',name+'::') retain=retain.replace('damage.', 'damage->') run(r""" #include #include enum class EDamageState{UNDAMAGED,DAMAGED,DESTROYED}; struct SCR_VehicleDamageManagerComponent{float health=1;bool engine=true,gear=true;EDamageState state=EDamageState::UNDAMAGED;float GetHealthScaled(){return health;}bool GetEngineFunctional(){return engine;}bool GetGearboxFunctional(){return gear;}EDamageState GetState(){return state;}}; struct IEntity{bool player=false,fit=true;IEntity* vehicle=nullptr;SCR_VehicleDamageManagerComponent* damage=nullptr;}; namespace ERR_Actors{bool Player(IEntity* a){return a->player;}bool CanFight(IEntity* a){return a->fit;}} namespace CompartmentAccessComponent{IEntity* GetVehicleIn(IEntity* a){return a->vehicle;}} struct Map:std::map{bool Contains(IEntity* a){return count(a);}IEntity* Get(IEntity* a){return at(a);}}s_Crews; """+retain+r""" int main(){ IEntity driver,truck,other;SCR_VehicleDamageManagerComponent health;truck.damage=&health;driver.vehicle=&truck; assert(!Retain(&driver,&truck));s_Crews[&driver]=&truck;assert(Retain(&driver,&truck));assert(!Retain(&driver,&other)); driver.player=true;assert(!Retain(&driver,&truck));driver.player=false;driver.fit=false;assert(!Retain(&driver,&truck));driver.fit=true; health.state=EDamageState::DAMAGED;health.health=.9;assert(Retain(&driver,&truck));health.health=.25;assert(!Retain(&driver,&truck));health.health=1; health.engine=false;assert(!Retain(&driver,&truck));health.engine=true;health.gear=false;assert(!Retain(&driver,&truck));health.gear=true; health.state=EDamageState::DESTROYED;assert(!Retain(&driver,&truck));health.state=EDamageState::UNDAMAGED;driver.vehicle=nullptr;assert(!Retain(&driver,&truck)); } """) boarding=body(D/'ERR_SupplyConvoyTactics.c','static AIWaypoint Board(') boarding=boarding.replace('AIWaypoint Board','AIWaypoint* Board').replace('Vehicle truck','Vehicle* truck').replace('truck.', 'truck->').replace('waypoint.', 'waypoint->').replace('specific.', 'specific->').replace('null','nullptr') boarding=boarding.replace('SCR_BoardingWaypoint waypoint','SCR_BoardingWaypoint* waypoint').replace('SCR_BoardingEntityWaypoint specific','SCR_BoardingEntityWaypoint* specific') boarding=boarding.replace('SCR_BoardingWaypoint.Cast(', 'dynamic_cast(').replace('SCR_BoardingEntityWaypoint.Cast(', 'dynamic_cast(').replace('ERR_Actors.Spawn','ERR_Actors::Spawn').replace('EAIWaypointCompletionType.All','EAIWaypointCompletionType::All') run(r""" #include #include using ResourceName=std::string; enum class EAIWaypointCompletionType{Leader,All}; struct Vehicle{int GetOrigin(){return 7;}}; struct AIWaypoint{virtual ~AIWaypoint(){}int radius=9;EAIWaypointCompletionType completion=EAIWaypointCompletionType::Leader;void SetCompletionRadius(int n){radius=n;}void SetCompletionType(EAIWaypointCompletionType type){completion=type;}}; struct SCR_BoardingWaypoint:AIWaypoint{bool driver=true,gunner=true,cargo=false;void SetAllowance(bool d,bool g,bool c){driver=d;gunner=g;cargo=c;}}; struct SCR_BoardingEntityWaypoint:SCR_BoardingWaypoint{Vehicle* vehicle=nullptr;void SetEntity(Vehicle* v){vehicle=v;}}; namespace ERR_Actors{bool fail=false;AIWaypoint* Spawn(ResourceName resource,int){if(fail)return nullptr;if(resource.find("GetIn.et")!=std::string::npos)return new SCR_BoardingEntityWaypoint();return new SCR_BoardingWaypoint();}} """+boarding+r""" int main(){ Vehicle truck;assert(!Board(nullptr,false)); auto out=dynamic_cast(Board(&truck,false));assert(out&&out->completion==EAIWaypointCompletionType::All&&out->radius==100&&!out->driver&&!out->gunner&&out->cargo); auto in=dynamic_cast(Board(&truck,true));assert(in&&in->radius==100&&in->completion==EAIWaypointCompletionType::All&&in->vehicle==&truck&&!in->driver&&!in->gunner&&in->cargo); delete out;delete in;ERR_Actors::fail=true;assert(!Board(&truck,true)); } """) delivery=body(D/'ERR_SupplyConvoyService.c','override bool ApplyCombatOrder(') delivery=delivery.replace('override ','').replace('ERR_SupplyConvoyTask.Cast(task)','dynamic_cast(task)') for typ in ('ERR_ConvoyTask','ERR_SupplyConvoyTask','ERR_ConvoyCombatOrder'):delivery=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',delivery) for name in ('supply','order'):delivery=delivery.replace(name+'.',name+'->') delivery=delivery.replace('Tactics.','Tactics->').replace('super.ApplyCombatOrder','Base::ApplyCombatOrder') run(r""" #include struct ERR_ConvoyTask{virtual ~ERR_ConvoyTask(){}}; struct ERR_ConvoyCombatOrder{bool Hold=true;int Position=3;float ObservedAt=100;}; struct Tactics{int state=0,reports=0;int State(){return state;}void Report(int,float,float){reports++;}}; struct ERR_SupplyConvoyTask:ERR_ConvoyTask{struct Tactics tactics;struct Tactics* Tactics=&tactics;}; struct Base{bool accept=true;int applied=0;bool ApplyCombatOrder(ERR_ConvoyTask*,ERR_ConvoyCombatOrder*,float){applied++;return accept;}}; struct Service:Base{ """+delivery+r""" }; int main(){ Service service;ERR_SupplyConvoyTask task;ERR_ConvoyCombatOrder order; for(int state=0;state<3;state++){task.tactics.state=state;assert(service.ApplyCombatOrder(&task,&order,100));} assert(task.tactics.reports==3);order.Hold=false;assert(!service.ApplyCombatOrder(&task,&order,100));assert(service.applied==3); task.tactics.state=0;assert(service.ApplyCombatOrder(&task,&order,100));assert(task.tactics.reports==3); service.accept=false;order.Hold=true;assert(!service.ApplyCombatOrder(&task,&order,100)&&task.tactics.reports==3); assert(!service.ApplyCombatOrder(nullptr,&order,100));assert(!service.ApplyCombatOrder(&task,nullptr,100)); } """) print('PASS: actual supply route cycling/arrival/stops, finite startup bounds, contact freshness, dismount hold, quiet interval, interrupted boarding, timeout and lost-truck release') # Mounted groups retain their native spawn origin; sighting distance must follow vehicles. distance=cpp(body(D/'ERR_SupplyConvoyTactics.c','protected float DistanceToConvoy(')).replace('Math.Min','Math::Min') observe=cpp(body(D/'ERR_SupplyConvoyTactics.c','protected bool Observe(')) for typ in ('SCR_AIGroup','SCR_AIGroupPerception','PerceptionManager','SCR_AITargetInfo'): observe=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',observe) for obj in ('group','perception','manager','target'):observe=observe.replace(obj+'.',obj+'->') observe=observe.replace('out vector position','vector& position').replace('inout float nearest','float& nearest').replace('bool found;','bool found=false;') observe=observe.replace('GetGroupUtilityComponent().','GetGroupUtilityComponent()->').replace('GetFaction().','GetFaction()->').replace('GetGame().','GetGame()->').replace('EAITargetInfoCategory.','EAITargetInfoCategory::') run(common+r""" namespace Math{float Min(float a,float b){return a Vehicles;}; struct Faction{bool hostile=true;bool IsFactionEnemy(Faction* other){return other->hostile;}}; enum class EAITargetInfoCategory{IDENTIFIED,SUSPECTED}; struct SCR_AITargetInfo{void* m_Entity=this;Faction* m_Faction=nullptr;float m_fTimestamp=10;EAITargetInfoCategory m_eCategory=EAITargetInfoCategory::IDENTIFIED;vector m_vWorldPos={5100};}; struct SCR_AIGroupPerception{Array m_aTargets;}; struct Utility{SCR_AIGroupPerception* m_Perception=nullptr;bool allowed=true;bool IsPositionAllowed(vector){return allowed;}}; struct SCR_AIGroup{Faction faction;Utility utility;Utility* GetGroupUtilityComponent(){return &utility;}Faction* GetFaction(){return &faction;}vector GetOrigin(){return {0};}}; struct PerceptionManager{float now=10;float GetTime(){return now;}}; struct Game{PerceptionManager manager;PerceptionManager* GetPerceptionManager(){return &manager;}}game; Game* GetGame(){return &game;} struct Observer{ERR_SupplyConvoyTask* m_Task; """+distance+observe+r""" }; int main(){ Entity car{{5000}},lost{{0}};ERR_ConvoyVehicle live{false,&car},released{true,&lost};ERR_SupplyConvoyTask task{{&live,&released}};Observer observer{&task}; Faction enemy;SCR_AITargetInfo target;target.m_Faction=&enemy;SCR_AIGroupPerception perception{{&target}};SCR_AIGroup group;group.utility.m_Perception=&perception; vector position;float nearest=400;assert(observer.Observe(&group,position,nearest)&&position.x==5100&&nearest==100); target.m_vWorldPos={100};nearest=400;assert(!observer.Observe(&group,position,nearest)); // stale group/spawn origin is irrelevant target.m_vWorldPos={5401};assert(!observer.Observe(&group,position,nearest));target.m_vWorldPos={5100}; target.m_eCategory=EAITargetInfoCategory::SUSPECTED;assert(!observer.Observe(&group,position,nearest));target.m_eCategory=EAITargetInfoCategory::IDENTIFIED; target.m_fTimestamp=4;assert(!observer.Observe(&group,position,nearest));target.m_fTimestamp=11;assert(!observer.Observe(&group,position,nearest));target.m_fTimestamp=5; assert(observer.Observe(&group,position,nearest));nearest=400;enemy.hostile=false;assert(!observer.Observe(&group,position,nearest));enemy.hostile=true; group.utility.allowed=false;assert(!observer.Observe(&group,position,nearest));group.utility.allowed=true; live.Released=true;assert(!observer.Observe(&group,position,nearest));assert(!observer.Observe(nullptr,position,nearest)); } """) retain_group=body(D/'ERR_SupplyConvoyTactics.c','static bool RetainGroup(') retain_group=retain_group.replace('SCR_AIGroup group','SCR_AIGroup* group').replace('IEntity vehicle','IEntity* vehicle').replace('IEntity leader','IEntity* leader').replace('group.','group->').replace('CompartmentAccessComponent.','CompartmentAccessComponent::') source=(D/'ERR_SupplyConvoyTactics.c').read_text() overrides=[] for cls in ('SCR_AIGetOutVehicle','SCR_AIGetOutActivity'): section=source[source.index('modded class '+cls):];section=section[:section.index('\nclass ') if '\nclass ' in section else len(section)] for method in ('CustomEvaluate','EvaluatePriorityLevel'): # Each method's exact production body, selecting only this class's first occurrence. a=section.index('override float '+method+'(');b=section.index('\n }',a)+3 method_source=section[a:b].replace('override ','').replace('m_Utility.','m_Utility->').replace('ERR_ConvoyMountedCrew.','ERR_ConvoyMountedCrew::').replace('super.','Base::') overrides.append(method_source) run(r""" #include struct IEntity{bool held=false;IEntity* vehicle=nullptr;}; struct SCR_AIGroup{int players=0;IEntity* leader=nullptr;int GetPlayerCount(bool){return players;}IEntity* GetLeaderEntity(){return leader;}}; namespace CompartmentAccessComponent{IEntity* GetVehicleIn(IEntity* actor){return actor->vehicle;}} struct ERR_ConvoyMountedCrew{static bool Retain(IEntity* actor,IEntity* vehicle){return actor&&actor->held&&vehicle&&actor->vehicle==vehicle;} """+retain_group+r""" }; struct Utility{IEntity* m_OwnerEntity;SCR_AIGroup* m_Owner;}; struct Base{float CustomEvaluate(){return 1162;}float EvaluatePriorityLevel(){return 1000;}}; struct Individual:Base{Utility* m_Utility=nullptr;struct{IEntity* m_Value=nullptr;}m_Vehicle; """+''.join(overrides[:2])+r""" }; struct Activity:Base{Utility* m_Utility=nullptr;struct{IEntity* m_Value=nullptr;}m_Vehicle; """+''.join(overrides[2:])+r""" }; int main(){ IEntity truck,driver{true,&truck};SCR_AIGroup group{0,&driver};Utility utility{&driver,&group};Individual individual;Activity activity; individual.m_Utility=&utility;individual.m_Vehicle.m_Value=&truck;activity.m_Utility=&utility;activity.m_Vehicle.m_Value=&truck; assert(individual.CustomEvaluate()==0&&individual.EvaluatePriorityLevel()==0);assert(activity.CustomEvaluate()==0&&activity.EvaluatePriorityLevel()==0); assert(ERR_ConvoyMountedCrew::RetainGroup(&group,nullptr));group.players=1;assert(activity.CustomEvaluate()==1162&&activity.EvaluatePriorityLevel()==1000);group.players=0; driver.held=false;assert(individual.CustomEvaluate()==1162&&individual.EvaluatePriorityLevel()==1000);assert(activity.CustomEvaluate()==1162&&activity.EvaluatePriorityLevel()==1000); group.leader=nullptr;assert(!ERR_ConvoyMountedCrew::RetainGroup(&group,&truck));assert(!ERR_ConvoyMountedCrew::RetainGroup(nullptr,&truck)); } """) print('PASS: moving-convoy perception boundaries and scoped individual/group abandonment priorities')