"""Actual convoy dispatch transaction and exact native seat-allocation policy.""" import os,re from pathlib import Path from policy_harness import ROOT,body,run path=Path(os.environ.get('ERR_CONVOY_SERVICE',str(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_ConvoyService.c'))) source=path.read_text() # Dispatch's native calls are doubled; budget behavior is executed separately. code=body(path,'ERR_ConvoyTask Dispatch(') code=re.sub(r'//[^\n]*','',code) code=code.replace('array','Array&') code=code.replace('SCR_AIGroup.Cast(', 'static_cast(').replace('Vehicle.Cast(', 'static_cast(') code=code.replace('FactionAffiliationComponent.Cast(unit.Entity.FindComponent(FactionAffiliationComponent))','&unit->Entity->affiliation').replace('AICarMovementComponent.Cast(unit.Entity.FindComponent(AICarMovementComponent))','&unit->Entity->movement') for cls in ['ERR_ConvoyTask','ERR_ConvoyReceipt','ERR_ForceRoute','ERR_ConvoyVehicle','FactionAffiliationComponent','AICarMovementComponent']: code=re.sub(r'\b'+cls+r' ([A-Za-z_]\w*)',cls+r'* \1',code) for obj in ['m_Mode','m_Pool','task','unit','affiliation','movement']: code=re.sub(r'\b'+obj+r'\.',obj+'->',code) for cls in ['ERR_ForceRules','ERR_ConvoyRules','ERR_ConvoyResources','ERR_Actors','ERR_HQ']: code=code.replace(cls+'.',cls+'::') code=code.replace('EnemyFaction().','EnemyFaction()->').replace('Group.Get','Group->Get').replace('null','nullptr') run(r''' #include #include #include using string=std::string;struct vector{}; templatestruct Array:std::vector{using std::vector::vector;void Insert(T v){this->push_back(v);}int Count(){return this->size();}}; struct Faction{string key="USSR";string GetFactionKey(){return key;}};Faction enemy; struct IEntity{virtual ~IEntity()=default;}; struct FactionAffiliationComponent{void SetAffiliatedFaction(Faction*){}}; struct AICarMovementComponent{int speed=0;void SetCruiseSpeed(int v){speed=v;}}; struct SCR_AIGroup:IEntity{Faction* GetFaction(){return &enemy;}int GetAgentsCount(){return 0;}}; struct Vehicle:IEntity{FactionAffiliationComponent affiliation;AICarMovementComponent movement;}; struct ERR_GameMode{Arrayowned;Faction* EnemyFaction(){return &enemy;}void Own(IEntity* e){owned.Insert(e);}}; struct ERR_ForceRoute{}; struct ERR_ConvoyReceipt{bool used=false;}; struct ERR_ConvoyPool{int consumes=0;bool Consume(ERR_ConvoyReceipt* r){if(!r||r->used)return false;r->used=true;consumes++;return true;}}; struct ERR_ConvoyVehicle{int Index=0;Vehicle* Entity=nullptr;SCR_AIGroup* Group=nullptr;}; struct ERR_ConvoyTask{ERR_ConvoyReceipt* Receipt=nullptr;ERR_ForceRoute* Route=nullptr;float Deadline=0;ArrayVehicles;bool stopped=false;void Stop(ERR_GameMode*,string){stopped=true;}}; namespace ERR_ForceRules{bool Finite(float f){return f==f;}} namespace ERR_ConvoyRules{bool valid=true,clear=true,empty=true;bool EmptyCrewTemplate(SCR_AIGroup*){return empty;}bool GroundClear(Array&,Array&){return clear;}bool Plan(Array&,Array&,ERR_ForceRoute*){return valid;}} namespace ERR_ConvoyResources{bool available=true;bool Available(){return available;}string GroupPrefab(){return "group";}string VehiclePrefab(int i){return "vehicle"+std::to_string(i);}} namespace ERR_HQ{void Event(string,string){}} namespace ERR_Actors{int calls=0,fail=0;IEntity* Spawn(string prefab,vector,vector=vector{}){calls++;if(calls==fail)return nullptr;if(prefab=="group")return new SCR_AIGroup();return new Vehicle();}} struct Service{ERR_GameMode* m_Mode;ERR_ConvoyPool* m_Pool;Arraym_Tasks;bool current=true;int crews=0,failCrew=-1; ERR_ConvoyTask* NewTask(){return new ERR_ConvoyTask();} ERR_ConvoyVehicle* NewVehicle(){return new ERR_ConvoyVehicle();} bool Current(){return current;}bool FillCrew(ERR_ConvoyVehicle* unit){crews++;return unit->Index!=failCrew;} ''' + code + r''' }; int main(){ ERR_GameMode mode;ERR_ConvoyPool pool;Service service{&mode,&pool};ERR_ConvoyReceipt receipt;ERR_ForceRoute route;Arraypositions(6),angles(6); ERR_ConvoyRules::valid=false;assert(!service.Dispatch(&receipt,positions,angles,&route,100));assert(!receipt.used&&ERR_Actors::calls==0);ERR_ConvoyRules::valid=true; ERR_ConvoyRules::clear=false;assert(!service.Dispatch(&receipt,positions,angles,&route,100));assert(!receipt.used&&ERR_Actors::calls==0);ERR_ConvoyRules::clear=true; ERR_ConvoyResources::available=false;assert(!service.Dispatch(&receipt,positions,angles,&route,100));assert(!receipt.used&&ERR_Actors::calls==0);ERR_ConvoyResources::available=true; enemy.key="US"; // A selected non-USSR enemy uses the same finite dispatch transaction. auto task=service.Dispatch(&receipt,positions,angles,&route,100);assert(task&&task->Vehicles.Count()==6&&!task->stopped&&receipt.used&&pool.consumes==1&&ERR_Actors::calls==12&&mode.owned.Count()==12&&service.crews==6); assert(!service.Dispatch(&receipt,positions,angles,&route,100));assert(ERR_Actors::calls==12); for(int failed=1;failed<=12;failed++){ ERR_GameMode partial;ERR_ConvoyPool budget;Service run{&partial,&budget};ERR_ConvoyReceipt ticket;ERR_Actors::calls=0;ERR_Actors::fail=failed; auto result=run.Dispatch(&ticket,positions,angles,&route,0);assert(result&&result->stopped&&ticket.used&&budget.consumes==1&&run.m_Tasks.Count()==1);int attempted=ERR_Actors::calls; assert(!run.Dispatch(&ticket,positions,angles,&route,1)&&ERR_Actors::calls==attempted);assert(partial.owned.Count()==attempted-1); } ERR_Actors::fail=0;ERR_ConvoyRules::empty=false;ERR_GameMode unexpected;ERR_ConvoyPool rejectBudget;Service wrongTemplate{&unexpected,&rejectBudget};ERR_ConvoyReceipt rejectTicket; auto rejected=wrongTemplate.Dispatch(&rejectTicket,positions,angles,&route,100);assert(rejected&&rejected->stopped&&rejectTicket.used&&wrongTemplate.crews==0);ERR_ConvoyRules::empty=true; ERR_Actors::fail=0;ERR_GameMode seats;ERR_ConvoyPool budget;Service noCrew{&seats,&budget};noCrew.failCrew=2;ERR_ConvoyReceipt ticket; auto partial=noCrew.Dispatch(&ticket,positions,angles,&route,100);assert(partial&&partial->stopped&&partial->Vehicles.Count()==3&&ticket.used&&seats.owned.Count()==6); } ''') # Execute the seat-selection and creation body. Failed capacity cannot shrink # the plan, and every native successful character is immediately owned. fill=body(path,'protected bool FillCrew(').replace('protected ','') fill=re.sub(r'//[^\n]*','',fill).replace('array','Array') for cls in ['ERR_ConvoyVehicle','BaseCompartmentSlot','AIGroup','IEntity']: fill=re.sub(r'\b'+cls+r' ([A-Za-z_]\w*)',cls+r'* \1',fill) for obj in ['unit','slot','seat','m_Mode']: fill=re.sub(r'\b'+obj+r'\.',obj+'->',fill) for cls in ['ERR_ConvoyRules','ECompartmentType','ERR_ConvoyPool','ERR_ConvoyResources']: fill=fill.replace(cls+'.',cls+'::') fill=fill.replace('foreach (','for (') run(r''' #include #include using ResourceName=int; templatestruct Array:std::vector{using std::vector::vector;void Insert(T v){this->push_back(v);}int Count(){return this->size();}}; enum class ECompartmentType{PILOT,TURRET,CARGO};struct IEntity{};struct AIGroup{}; struct BaseCompartmentSlot{ECompartmentType type;bool occupied=false,accessible=true,fail=false;int spawns=0;ECompartmentType GetType(){return type;}bool IsCompartmentAccessible(){return accessible;}bool IsOccupied(){return occupied;}IEntity* SpawnCharacterInCompartment(ResourceName,AIGroup*&,ResourceName){spawns++;return fail?nullptr:new IEntity();}}; struct Vehicle{Arrayseats;}; struct ERR_ConvoyVehicle{int Index=0;Vehicle* Entity;AIGroup* Group;ArraySeats;ArrayMembers;IEntity* Driver=nullptr;}; namespace ERR_ConvoyRules{bool CollectSeats(Vehicle* v,Array& out){out=v->seats;return true;}} namespace ERR_ConvoyPool{int CrewCount(int i){if(i==1)return 1;if(i==2)return 9;if(i==4)return 3;return 2;}} namespace ERR_ConvoyResources{int Soldier(){return 1;}int GroupPrefab(){return 2;}} struct Mode{int owned=0;void Own(IEntity*){owned++;}}; struct Service{Mode* m_Mode; ''' + fill + r''' }; int main(){ for(int i=0;i<6;i++){ Vehicle vehicle;AIGroup group;Mode mode;Service service{&mode};ERR_ConvoyVehicle unit;unit.Index=i;unit.Entity=&vehicle;unit.Group=&group; BaseCompartmentSlot driver{ECompartmentType::PILOT},gunner{ECompartmentType::TURRET},commander{ECompartmentType::TURRET};vehicle.seats={&driver,&gunner,&commander}; BaseCompartmentSlot seats[8];for(auto& seat:seats){seat.type=ECompartmentType::CARGO;vehicle.seats.Insert(&seat);} assert(service.FillCrew(&unit));assert(unit.Members.Count()==ERR_ConvoyPool::CrewCount(i)&&mode.owned==unit.Members.Count()&&unit.Driver==unit.Members[0]); } Vehicle vehicle;AIGroup group;Mode mode;Service service{&mode};ERR_ConvoyVehicle unit;unit.Index=2;unit.Entity=&vehicle;unit.Group=&group; BaseCompartmentSlot driver{ECompartmentType::PILOT};vehicle.seats={&driver};assert(!service.FillCrew(&unit)&&driver.spawns==0&&mode.owned==0); BaseCompartmentSlot seats[8];for(auto& seat:seats){seat.type=ECompartmentType::CARGO;vehicle.seats.Insert(&seat);}seats[3].fail=true; assert(!service.FillCrew(&unit)&&unit.Members.Count()==4&&mode.owned==4);assert(seats[4].spawns==0); } ''') print('PASS: actual convoy dispatch validates before receipt consumption, consumes once before native mutation, retains all12 partial-failure paths, accepts the selected non-USSR enemy, and allocates exact19 bodies without capacity shrinking') # The resource must suppress authored allocation before native EOnInit, not try # to purge a queued default squad after custom seat occupants have been added. prefab=ROOT/'addons/core/Prefabs/ERR/ConvoyCrew.et' text=prefab.read_text() assert '"{000CD338713F2B5A}Prefabs/AI/Groups/Group_Base.et"' in text assert 'm_bSpawnImmediately 0' in text and 'm_faction "USSR"' in text assert re.search(r'm_aUnitPrefabSlots\s*{\s*}',text) assert 'Group_USSR_Base.et' not in text identity=re.search(r'Name "([^"\n]+)"',prefab.with_name(prefab.name+'.meta').read_text())[1] assert identity in body(path,'static ResourceName GroupPrefab(') empty=body(path,'static bool EmptyCrewTemplate(') empty=empty.replace('SCR_AIGroup group','SCR_AIGroup* group').replace('group.','group->').replace('m_aUnitPrefabSlots.IsEmpty()', 'm_aUnitPrefabSlots->IsEmpty()') run(r''' #include struct Slots{bool empty=true;bool IsEmpty(){return empty;}}; struct SCR_AIGroup{bool immediate=false;int count=0;Slots* m_aUnitPrefabSlots=nullptr;bool GetSpawnImmediately(){return immediate;}int GetAgentsCount(){return count;}}; '''+empty+r''' int main(){SCR_AIGroup group;Slots slots;assert(EmptyCrewTemplate(&group));group.m_aUnitPrefabSlots=&slots;assert(EmptyCrewTemplate(&group));slots.empty=false;assert(!EmptyCrewTemplate(&group));slots.empty=true;group.immediate=true;assert(!EmptyCrewTemplate(&group));group.immediate=false;group.count=1;assert(!EmptyCrewTemplate(&group));assert(!EmptyCrewTemplate(nullptr));} ''') print('PASS: own memberless prefab disables automatic spawn before initialization; actual admission rejects populated, immediate or authored-slot templates before finite crew creation')