"""Execute recovery spawn retries and retirement using production method bodies.""" from policy_harness import ROOT, body, run p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_BodyRecovery.c' def adapt(s): s=s.replace('Math.Max', 'std::max').replace('protected ', '').replace('ERR_BodyRecoveryJob job','Job* job').replace('AIAgent agent','Agent* agent') s=s.replace('ChimeraAIWorld world = ChimeraAIWorld.Cast(GetGame().GetAIWorld());','World* world = GetGame()->GetAIWorld();') s=s.replace('job.Team.GetOnAgentAdded().Remove(job.MemberAdded);','job.Team.GetOnAgentAdded()->Remove();') s=s.replace('new array()','Slots{0}').replace('array agents = {};','Array agents;') s=s.replace('foreach (','for (').replace('null','nullptr') s=s.replace('ERR_HQ.Event','ERR_HQ::Event').replace('string.Format','string::Format') for v in ('job','agent','world','m_HQ','m_Mode'): s=s.replace(v+'.',v+'->') s=s.replace('Team.','Team->').replace('Evidence().','Evidence()->') return s added=adapt(body(p,'void MemberAdded(')) retry=adapt(body(p,'protected void RetryEmptySpawn(')) refund=adapt(body(p,'protected void ReturnUnusedWave(')) preparation=adapt(body(p,'protected bool RetryPreparation(')) retire=adapt(body(p,'protected void RetireSpawn(')) finish=adapt(body(p,'protected void Finish(')) pins=adapt(body(p,'protected void ReleasePins(')) diagnostic=adapt(body(p,'protected void Diagnostic(')).split(' int agents, living;')[0] diagnostic += ' diagnostics++;\n }' reuse=adapt(body(p,'protected bool Reusable(')).replace('ERR_BodyRecoveryJob previous','Job* previous').replace('previous.', 'previous->') run(r''' #include #include #include #include struct string:std::string{using std::string::string;templatestatic string Format(string fmt,T...){return fmt;}}; templatestruct Array:std::vector{int Count(){return this->size();}}; struct Agent{int lod=-1,activations=0;void SetPermanentLOD(int v){lod=v;}void ActivateAI(){activations++;}}; struct Slots{int size=4;int Count(){return size;}}; struct Hook{int removals=0;void Remove(){removals++;}}; struct Group{int count=0,lod=0,requests=0,wanted=0;bool immediately=true;int players=0,faction=1,living=4;int GetPlayerCount(bool){return players;}int GetFaction(){return faction;}Slots m_aUnitPrefabSlots;Hook hook;Array agents; int GetAgentsCount(){return count;}void RequestSpawn(int n){requests++;wanted=n;}void SetSpawnImmediately(bool b){immediately=b;} Hook* GetOnAgentAdded(){return &hook;}void SetPermanentLOD(int n){lod=n;}void GetAgents(Array& out){out=agents;}}; struct World{int purges=0;Group* last=nullptr;void PurgeSpawnRequestsForGroup(Group*g){purges++;last=g;}}; struct Game{World world;bool ai=true;World* GetAIWorld(){return ai?&world:nullptr;}} game; bool gameAvailable=true;Game* GetGame(){return gameAvailable?&game:nullptr;} struct Job{int Id=1,State=0,SpawnAttempts=1;float NextSpawnAttempt=10000;bool SpawnedMember=false,Delivered=false,Prepared=false,SpawnWave=false,HadTeam=false;int PreparationRetries=0;float RetryAt=0;float NextStatusDiagnostic=0,NextDiagnostic=0;string LastDiagnosticReason;Group* Team=nullptr;Array Missing;int Position=99,Body=7;void* Order=nullptr;void* Report=nullptr; '''+added+r''' }; struct Evidence{int delivered=0;void Delivered(int){delivered++;}}; struct Mode{int EnemyFaction(){return 1;}int ForceLiving(Group*g){return g->living;}struct Evidence evidence;struct Evidence* Evidence(){return &evidence;}}; struct ERR_HQ{int compromised=0,released=0;static void Event(string,string){}void CompromisedSites(Array,int){compromised++;}void ReleaseRecovery(Group*){released++;}}; struct Recovery{Mode* m_Mode;ERR_HQ* m_HQ;int removed=0,diagnostics=0,m_Waves=0,m_SpawnRequests=0;void RemoveWaypoint(Job*){removed++;} '''+retry+retire+pins+refund+preparation+finish+reuse+diagnostic+r''' }; int main(){ Mode mode;ERR_HQ hq;Recovery r{&mode,&hq};Group team;Job job;job.Team=&team; r.RetryEmptySpawn(&job,9999);assert(team.requests==0&&job.SpawnAttempts==1); r.RetryEmptySpawn(&job,10000);assert(team.requests==1&&team.wanted==4&&job.SpawnAttempts==2&&game.world.last==&team); r.RetryEmptySpawn(&job,19999);assert(team.requests==1); r.RetryEmptySpawn(&job,20000);assert(team.requests==2&&job.SpawnAttempts==3); r.RetryEmptySpawn(&job,90000);assert(team.requests==2&&game.world.purges==2); // Unchanged waits are rate limited; changed causes are visible immediately. Job status;r.Diagnostic(&status,0,"roster");assert(r.diagnostics==1); r.Diagnostic(&status,29999,"roster");assert(r.diagnostics==1); r.Diagnostic(&status,29999,"radio");assert(r.diagnostics==2); status.NextDiagnostic=1000000; // movement logs have a separate throttle r.Diagnostic(&status,59999,"radio");assert(r.diagnostics==3); // Never replenish a soldier that spawned and disappeared before the next poll. Job lost;lost.Team=&team;Agent original;lost.MemberAdded(&original);assert(lost.SpawnedMember&&original.lod==0&&original.activations==1); r.RetryEmptySpawn(&lost,90000);assert(team.requests==2); Job partial;partial.Team=&team;team.count=1;r.RetryEmptySpawn(&partial,90000);assert(team.requests==2);team.count=0; Job callback;callback.Team=&team;callback.MemberAdded(nullptr);r.RetryEmptySpawn(&callback,90000);assert(team.requests==2); // Abort cancels pending spawn intent and clears orders, but never reports intel. job.Order=&team;job.Report=&team;team.agents.push_back(&original); r.Finish(&job,false,"preparation deadline");assert(job.State==4&&!job.Delivered&&!job.Order&&!job.Report); assert(team.m_aUnitPrefabSlots.Count()==0&&!team.immediately&&game.world.purges==3&&team.hook.removals==1&&team.lod==-1&&original.lod==-1); assert(!mode.evidence.delivered&&!hq.compromised&&hq.released==1); // Only actual successful receipt calls the evidence/compromise boundary. Job received;received.Team=&team;received.State=3;received.Missing.push_back("radio-1"); r.Finish(&received,true,"radio delivered");assert(received.Delivered&&mode.evidence.delivered==1&&hq.compromised==1); assert(game.world.purges==3); // a completed registered team has no preparation queue to cancel Job deleted;r.Finish(&deleted,false,"team deleted");assert(deleted.State==4&&!deleted.Delivered); // A failed unregistered preparation may leave survivors, but cannot be reused // as a registered team. Completed ready teams may be reused; ownership holds. Job waiting,previous;previous.Team=&team;previous.State=4; assert(!r.Reusable(&waiting,&previous));previous.Prepared=true; assert(r.Reusable(&waiting,&previous)&&!r.Reusable(&previous,&previous)); previous.State=2;assert(!r.Reusable(&waiting,&previous));previous.State=4; team.players=1;assert(!r.Reusable(&waiting,&previous));team.players=0; team.faction=2;assert(!r.Reusable(&waiting,&previous));team.faction=1; team.living=0;assert(!r.Reusable(&waiting,&previous));team.living=1; assert(r.Reusable(&waiting,&previous));previous.Team=nullptr;assert(!r.Reusable(&waiting,&previous)); // Failed empty attempts do not consume the allowance for actual teams. Job empty;empty.Team=&team;empty.SpawnWave=true;r.m_Waves=1; r.ReturnUnusedWave(&empty);assert(r.m_Waves==0&&!empty.SpawnWave); r.ReturnUnusedWave(&empty);assert(r.m_Waves==0); empty.SpawnWave=true;empty.SpawnedMember=true;r.m_Waves=1; r.ReturnUnusedWave(&empty);assert(r.m_Waves==1&&empty.SpawnWave); empty.SpawnedMember=false;team.count=1; r.ReturnUnusedWave(&empty);assert(r.m_Waves==1);team.count=0; empty.PreparationRetries=2;assert(!r.RetryPreparation(&empty,100000)); empty.PreparationRetries=0;r.m_SpawnRequests=32;assert(!r.RetryPreparation(&empty,100000)); r.m_SpawnRequests=8;empty.SpawnedMember=true;assert(!r.RetryPreparation(&empty,100000)); empty.SpawnedMember=false;int purges=game.world.purges; assert(r.RetryPreparation(&empty,100000)); assert(!empty.Team&&!empty.HadTeam&&empty.RetryAt==160000&&empty.PreparationRetries==1&&r.m_Waves==0); assert(game.world.purges==purges+1); // Teardown can retire intent after the game/AI-world accessor has disappeared. Job stop;stop.Team=&team;gameAvailable=false;r.RetireSpawn(&stop);assert(!team.immediately); gameAvailable=true;game.ai=false;int previousPurges=game.world.purges;r.RetireSpawn(&stop);assert(game.world.purges==previousPurges); } ''') print('PASS: bounded same-group empty-spawn retries, no casualty refill, queue retirement, delivery-gated intel, prepared-team reuse, throttled diagnostics and LOD release')