"""Production hunter release and objective-role policy, without native spawning.""" import os from pathlib import Path from policy_harness import ROOT, body, run source=Path(os.environ.get('ERR_HUNTER_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HunterService.c')) s=body(source,'override void Release(ERR_GameMode mode)') s=s.replace('override ','').replace('super.','Native::').replace('null','nullptr') s=s.replace('ERR_GameMode mode','ERR_GameMode* mode').replace('ERR_ForceRecord record','ERR_ForceRecord* record') s=s.replace('ERR_HQ hq = ERR_HQ.Cast(mode.Command())', 'ERR_HQ* hq = mode->Command()') s=s.replace('array agents = {};','Array agents;').replace('foreach (AIAgent agent : agents)','for (AIAgent* agent : agents)') for name in ('mode','record','Group','agent','hq','Recovery','Tactics','CloseContact'):s=s.replace(name+'.',name+'->') s=s.replace('Forces().','Forces()->').replace('ERR_ConvoyRules.','ERR_ConvoyRules::') objective=body(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_ObjectiveDefence.c','static bool Objective(').replace('ERR_LocationKind.','ERR_LocationKind::') run(r''' #include #include templateusing Array=std::vector; struct AIAgent{int lod=0,writes=0;void SetPermanentLOD(int x){lod=x;writes++;}}; struct GroupType{int lod=0;AIAgent agent;void SetPermanentLOD(int x){lod=x;}void GetAgents(Array& a){a={&agent};}}; struct ERR_ForceRecord{bool Pinned=true;}; struct Ledger{ERR_ForceRecord record;ERR_ForceRecord* FindGroup(GroupType*){return &record;}}; struct ERR_HQ{int releases=0;void ReleaseRecovery(GroupType*){releases++;}}; struct ERR_GameMode{bool active=true;Ledger ledger;ERR_HQ hq;bool Active(){return active;}Ledger* Forces(){return &ledger;}ERR_HQ* Command(){return &hq;}}; namespace ERR_ConvoyRules{bool captured=false;bool Captured(void*,GroupType*){return captured;}} struct RecoveryTask{int stops=0;void Stop(){stops++;}void Stop(ERR_GameMode*){stops++;}}; struct Native{RecoveryTask* Recovery=nullptr;RecoveryTask* Tactics=nullptr;RecoveryTask* CloseContact=nullptr;bool Released=false,PlayerCaptureObserved=false;int delegated=0,clears=0;GroupType* Group=nullptr;void* Entity=nullptr;void* Downlink=nullptr;void CancelHQDownlink(){Downlink=nullptr;} void Release(ERR_GameMode*){delegated++;Released=true;}void ClearOrder(){clears++;} }; struct Hunter:Native{ '''+s+r''' }; namespace ERR_LocationKind{enum{PRISON,RADIO,EXTRACTION,PATROL,DEPOT,OUTPOST,MORTAR,HOSPITAL};} '''+objective+r''' int main(){ ERR_GameMode mode;GroupType group;Hunter unit;unit.Group=&group;unit.Entity=&group;unit.Downlink=&mode;RecoveryTask recovery,close;unit.Recovery=&recovery;unit.CloseContact=&close; unit.Release(&mode);assert(unit.Released&&!unit.Downlink&&unit.clears==1&&unit.delegated==0); assert(recovery.stops==1&&close.stops==1); assert(!mode.ledger.record.Pinned&&mode.hq.releases==1&&group.lod==-1&&group.agent.writes==1); assert(unit.Entity==&group&&unit.Group==&group); // living and dead actors/vehicle are preserved unit.Release(&mode);assert(unit.clears==1&&mode.hq.releases==1&&group.agent.writes==1); Hunter captured;ERR_ConvoyRules::captured=true;captured.Release(&mode);assert(captured.delegated==1&&mode.hq.releases==1); ERR_ConvoyRules::captured=false;Hunter stopped;mode.active=false;stopped.Release(&mode);assert(stopped.delegated==1); Hunter missing;missing.Release(nullptr);assert(missing.delegated==1); assert(Objective(ERR_LocationKind::RADIO)&&Objective(ERR_LocationKind::DEPOT)&&Objective(ERR_LocationKind::OUTPOST)&&Objective(ERR_LocationKind::EXTRACTION)); assert(!Objective(ERR_LocationKind::PRISON)&&!Objective(ERR_LocationKind::PATROL)&&!Objective(ERR_LocationKind::MORTAR)); } ''') print('PASS: actual hunter release preserves survivors/HQ, releases driving ownership once, delegates capture/teardown, and excludes prison/patrol/mortar from generic objective defence') # Execute the cancellation bridge itself, including teardown with unavailable HQ/world. cancel=body(source,'void CancelHQDownlink()').replace('ERR_HQ hq','ERR_HQ* hq').replace('ERR_HQ.Get()', 'ERR_HQ::Get()').replace('hq.', 'hq->').replace('Downlink.', 'Downlink->').replace('GetGame().GetWorld().GetWorldTime()', 'GetGame()->GetWorld()->GetWorldTime()').replace('GetGame().GetWorld()', 'GetGame()->GetWorld()').replace('null','nullptr') run(r''' #include #include using string=std::string; struct World{float GetWorldTime(){return 1234;}}; struct Game{World world;World*current=&world;World*GetWorld(){return current;}}game; Game*currentGame=&game;Game*GetGame(){return currentGame;} struct ERR_HQ{inline static ERR_HQ*current=nullptr;int calls=0,message=0;string source;float at=0;static ERR_HQ*Get(){return current;}void CoordinatedHunterDelivery(string s,int m,bool delivered,float now){assert(!delivered);calls++;source=s;message=m;at=now;}}; struct Route{int MessageId=77;}; struct Hunter{string Source="original-hunter";Route*Downlink=nullptr; '''+cancel+r''' }; int main(){ERR_HQ hq;ERR_HQ::current=&hq;Hunter unit;Route route;unit.Downlink=&route; unit.CancelHQDownlink();assert(!unit.Downlink&&hq.calls==1&&hq.message==77&&hq.source==unit.Source&&hq.at==1234); unit.CancelHQDownlink();assert(hq.calls==1); for(int missing=0;missing<3;missing++){ ERR_HQ::current=&hq;currentGame=&game;game.current=&game.world;unit.Downlink=&route; if(missing==0)ERR_HQ::current=nullptr; if(missing==1)currentGame=nullptr; if(missing==2)game.current=nullptr; unit.CancelHQDownlink();assert(!unit.Downlink&&hq.calls==1); } } ''') print('PASS: actual hunter cancellation releases exactly the pending source/message once; teardown clears ownership without HQ/game/world')