Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
+108
View File
@@ -0,0 +1,108 @@
"""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<ResourceName>()','Slots{0}').replace('array<AIAgent> agents = {};','Array<Agent*> 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 <cassert>
#include <algorithm>
#include <string>
#include <vector>
struct string:std::string{using std::string::string;template<class...T>static string Format(string fmt,T...){return fmt;}};
template<class T>struct Array:std::vector<T>{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<Agent*> 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<Agent*>& 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<string> 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<string>,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')