Files
EscapistsReintroduced/tests/test_intel_body_inspection.py
T
2026-10-20 16:13:00 +02:00

166 lines
13 KiB
Python

"""Execute the production physical body-inspection gate; engine traces are boundary doubles."""
from policy_harness import ROOT, body, run
import re
p=ROOT/'addons/core/Scripts/Game/ERR/20_Intel/ERR_IntelEvidence.c'
s='\n'.join(body(p,sig) for sig in ('bool Inspect(', 'bool RetainedWithin(', 'void Delivered(', 'void Release('))
for old,new in [('out array<string> missing','Array<string>& missing'),('array<IEntity>','Array<IEntity>'),('array<string>','Array<string>'),('foreach (','for ('),('ERR_BodyEvidence record','ERR_BodyEvidence* record'),('ERR_IssuedIntel issued','ERR_IssuedIntel* issued'),('TraceParam trace','TraceParam* trace'),('InventoryStorageManagerComponent inventory','InventoryStorageManagerComponent* inventory'),('SCR_GarbageSystem garbage','SCR_GarbageSystem* garbage'),('null','nullptr')]:s=s.replace(old,new)
s=s.replace('InventoryStorageManagerComponent.Cast(body.FindComponent(InventoryStorageManagerComponent))','body->inventory')
for cls in ('Replication','ERR_GameMode','ERR_Actors','CompartmentAccessComponent','vector','TraceFlags','ERR_DiscoveryService','SCR_GarbageSystem'):
s=s.replace(cls+'.',cls+'::')
s=s.replace('parent.GetParent()', 'parent->GetParent()')
for v in ('body','observer','record','issued','trace','garbage'):
s=s.replace(v+'.',v+'->')
s=s.replace('ERR_GameMode::Get().','ERR_GameMode::Get()->').replace('GetGame().GetWorld().','GetGame()->GetWorld()->')
run(r'''
#include <cassert>
#include <string>
#include <vector>
#include <algorithm>
#include <cmath>
using string=std::string;
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;void Clear(){this->clear();}bool Contains(T t){return std::find(this->begin(),this->end(),t)!=this->end();}void Insert(T t){this->push_back(t);}};
struct vector {float x=0,y=0,z=0;vector()=default;vector(float a,float b,float c):x(a),y(b),z(c){}vector(const char*){}vector operator+(vector v){return {x+v.x,y+v.y,z+v.z};}static float Distance(vector a,vector b){return std::hypot(std::hypot(a.x-b.x,a.y-b.y),a.z-b.z);}};
struct Entity;using IEntity=Entity*;
struct InventoryStorageManagerComponent{Array<IEntity> folders;};
struct Entity{IEntity parent=nullptr;IEntity GetParent(){return parent;}bool alive=true,fit=true,player=false,seated=false;InventoryStorageManagerComponent* inventory=nullptr;vector position;vector GetOrigin(){return position;}};
struct ERR_Actors{static bool Alive(IEntity e){return e&&e->alive;}static bool CanFight(IEntity e){return e&&e->alive&&e->fit;}static bool Player(IEntity e){return e&&e->player;}};
struct CompartmentAccessComponent{static bool GetVehicleIn(IEntity e){return e->seated;}};
struct TraceFlags{enum{ENTS=1,WORLD=2};};struct TraceParam{vector Start,End;IEntity Exclude=nullptr,TraceEnt=nullptr;int Flags=0;};
struct World{float fraction=1;IEntity hit=nullptr;float TraceMove(TraceParam* t,void*){t->TraceEnt=hit;return fraction;}};
struct Game{World world;World* GetWorld(){return &world;}} game;Game* GetGame(){return &game;}
struct Replication{inline static bool server=true;static bool IsServer(){return server;}};
struct ERR_GameMode{bool active=true;static ERR_GameMode* Get(){static ERR_GameMode mode;return &mode;}bool Active(){return active;}};
struct ERR_DiscoveryService{static void Folders(InventoryStorageManagerComponent* inv,Array<IEntity>& folders){folders=inv->folders;}};
struct ERR_IssuedIntel{IEntity Folder;string SiteId;};
struct ERR_BodyEvidence{IEntity Actor;Array<ERR_IssuedIntel*> Issued;bool Searched=false;bool Reported=false;};
struct SCR_GarbageSystem {
inline static int releases=0,inserts=0;inline static bool blacklisted=true;inline static float delay=0;
static SCR_GarbageSystem* GetByEntityWorld(IEntity){static SCR_GarbageSystem g;return &g;}
void UpdateBlacklist(IEntity,bool value){blacklisted=value;releases++;}
void Insert(IEntity,float value){assert(!blacklisted);inserts++;delay=value;}
};
struct Evidence{Array<ERR_BodyEvidence*> m_Roster;bool m_Stopped=false;ERR_BodyEvidence* record=nullptr;ERR_BodyEvidence* Find(IEntity b){return record&&record->Actor==b?record:nullptr;}
'''+s+r'''
};
int main(){
for(int invalid=0;invalid<9;invalid++){
Entity body,observer,folder;body.alive=false;InventoryStorageManagerComponent inventory;body.inventory=&inventory;
ERR_IssuedIntel issued{&folder,"radio-1"};ERR_BodyEvidence record{&body,{&issued},false};Evidence service;service.record=&record;Array<string> result;
game.world.fraction=1;Replication::server=true;ERR_GameMode::Get()->active=true;
if(invalid==0)Replication::server=false;
if(invalid==1)service.m_Stopped=true;
if(invalid==2)body.alive=true;
if(invalid==3)observer.player=true;
if(invalid==4)observer.fit=false;
if(invalid==5)observer.seated=true;
if(invalid==6)observer.position={3.01f,0,0};
if(invalid==7)game.world.fraction=.5;
if(invalid==8)body.inventory=nullptr;
assert(!service.Inspect(&body,&observer,result)&&!record.Searched&&result.empty());
}
Replication::server=true;game.world.fraction=1;
Entity body,observer,present,missing;body.alive=false;InventoryStorageManagerComponent inv;body.inventory=&inv;inv.folders.Insert(&present);
ERR_IssuedIntel a{&present,"radio-1"},b{&missing,"mortar-3"},c{nullptr,"depot-1"};ERR_BodyEvidence record{&body,{&a,&b,&c},false};Evidence service;service.record=&record;Array<string> result;
observer.position={3,0,0};assert(service.Inspect(&body,&observer,result));assert(result.size()==2&&result.Contains("mortar-3")&&result.Contains("depot-1")&&!result.Contains("radio-1"));
assert(!service.Inspect(&body,&observer,result));
// Engine deletion clears the weak entity reference, not the issued site.
// A consumed folder must remain reportable as missing after physical cleanup.
record.Searched=false;b.Folder=nullptr;
assert(service.Inspect(&body,&observer,result)&&result.Contains("mortar-3"));
Entity wreck,seat,unrelated;body.parent=&seat;seat.parent=&wreck;service.m_Roster={&record};
assert(service.RetainedWithin(&body)&&service.RetainedWithin(&seat)&&service.RetainedWithin(&wreck));
assert(!service.RetainedWithin(&unrelated)&&!service.RetainedWithin(nullptr));
body.alive=true;assert(!service.RetainedWithin(&wreck));body.alive=false;
record.Reported=true;assert(!service.RetainedWithin(&wreck));record.Reported=false;
service.m_Stopped=true;assert(!service.RetainedWithin(&wreck));service.m_Stopped=false;
// Searching protects the body until HQ receives the report. Premature,
// client-side, stopped and repeated deliveries never release/renew cleanup.
record.Searched=false;service.Delivered(&body);assert(!record.Reported&&SCR_GarbageSystem::inserts==0);
record.Searched=true;Replication::server=false;service.Delivered(&body);assert(!record.Reported);
Replication::server=true;service.m_Stopped=true;service.Delivered(&body);assert(!record.Reported);
service.m_Stopped=false;service.Delivered(&unrelated);assert(!record.Reported);
assert(service.RetainedWithin(&body));service.Delivered(&body);
assert(record.Reported&&!service.RetainedWithin(&body));
assert(SCR_GarbageSystem::inserts==1&&SCR_GarbageSystem::releases==1&&SCR_GarbageSystem::delay==300);
service.Delivered(&body);assert(SCR_GarbageSystem::inserts==1&&SCR_GarbageSystem::releases==1);
record.Reported=false;
// Corrupt/cyclic parent links cannot cause unbounded cleanup traversal.
seat.parent=&seat;assert(!service.RetainedWithin(&wreck));
}
''')
print('PASS: physical inspection and parent-wreck retention; server-only searched/report delivery unlocks native cleanup once with a 300-second delay')
# Exercise sealing as well as corpse inspection: a large camp population should
# cover unfinished sites, while already-issued and authored clues stay fixed.
selection='\n'.join(body(p,sig) for sig in ('protected string NextTarget(', 'void Capture('))
selection=selection.replace('protected ', '').replace('foreach (', 'for (').replace('array<', 'Array<')
selection=selection.replace('Array<ERR_LocationComponent>', 'Array<ERR_LocationComponent*>')
selection=selection.replace('InventoryStorageManagerComponent.Cast(actor.FindComponent(InventoryStorageManagerComponent))', 'actor->inventory')
selection=selection.replace('ERR_DocumentComponent.Cast(folder.FindComponent(ERR_DocumentComponent))', 'folder->document')
for t in ('ERR_BodyEvidence','ERR_IssuedIntel','InventoryStorageManagerComponent','ERR_LocationComponent','ERR_DocumentComponent'):
selection=re.sub(r'\b'+t+r' (\w+)', t+r'* \1', selection)
for n in ('location','record','issued','item','document'):
selection=selection.replace(n+'.',n+'->')
for n in ('ERR_GameMode','ERR_Actors','ERR_DiscoveryService','ERR_LocationKind'):
selection=selection.replace(n+'.',n+'::')
selection=selection.replace('ERR_GameMode::Get().','ERR_GameMode::Get()->').replace('GetRandomElement().Id()', 'GetRandomElement()->Id()')
selection=selection.replace('bool known;', 'bool known=false;')
run(r'''
#include <cassert>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
struct string:std::string{using std::string::string;using std::string::operator=;string(std::string s):std::string(s){}bool IsEmpty(){return empty();}};
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;void Clear(){this->clear();}int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Insert(T t){this->push_back(t);}T GetRandomElement(){return this->front();}};
template<class K,class V>struct Map:std::map<K,V>{bool Find(K k,V& v){auto i=this->find(k);if(i==this->end())return false;v=i->second;return true;}void Set(K k,V v){(*this)[k]=v;}};
namespace ERR_LocationKind{enum{PRISON,EXTRACTION,RADIO,OUTPOST};}
struct ERR_LocationComponent{string id;int knowledge=0,kind=ERR_LocationKind::OUTPOST;string Id(){return id;}int Knowledge(){return knowledge;}int Kind(){return kind;}};
struct ERR_GameMode{Array<ERR_LocationComponent*> locations;static ERR_GameMode*Get(){static ERR_GameMode m;return &m;}void Locations(Array<ERR_LocationComponent*>&out){out=locations;}};
struct ERR_DocumentComponent{string target;string TargetId(){return target;}};
struct Entity;using IEntity=Entity*;
struct InventoryStorageManagerComponent{Array<IEntity>folders;};
struct Entity{bool alive=true,player=false;InventoryStorageManagerComponent*inventory=nullptr;ERR_DocumentComponent*document=nullptr;};
namespace ERR_Actors{bool Alive(IEntity e){return e&&e->alive;}bool Player(IEntity e){return e&&e->player;}}
struct ERR_IssuedIntel{IEntity Folder=nullptr;string SiteId;};
struct ERR_BodyEvidence{IEntity Actor=nullptr;bool Searched=false;Array<ERR_IssuedIntel*>Issued;};
namespace ERR_DiscoveryService{void Folders(InventoryStorageManagerComponent*inv,Array<IEntity>&out){out=inv->folders;}}
struct Evidence{Map<string,int>m_TargetAssignments;ERR_BodyEvidence*record=nullptr;ERR_BodyEvidence*Find(IEntity actor){return record&&record->Actor==actor?record:nullptr;}
'''+selection+r'''
};
int main(){
auto mode=ERR_GameMode::Get();Evidence service;ERR_LocationComponent sites[60];
for(int i=0;i<60;i++){sites[i].id="camp-"+std::to_string(i);mode->locations.Insert(&sites[i]);}
// Known sites and automatic prison/extraction disclosures are never new targets.
sites[0].knowledge=2;sites[1].kind=ERR_LocationKind::PRISON;sites[2].kind=ERR_LocationKind::EXTRACTION;
sites[3].knowledge=1;mode->locations.Insert(nullptr);
for(int i=0;i<570;i++)assert(!service.NextTarget().IsEmpty());
assert(service.m_TargetAssignments.size()==57);
for(int i=3;i<60;i++)assert(service.m_TargetAssignments[sites[i].id]==10);
// New generated camps catch up instead of leaving all intel on the early sites.
ERR_LocationComponent added;added.id="late-camp";mode->locations.Insert(&added);
for(int i=0;i<10;i++)assert(service.NextTarget()=="late-camp");
sites[3].knowledge=2;
for(int i=0;i<56;i++)assert(service.NextTarget()!=sites[3].id);
assert(service.m_TargetAssignments[sites[3].id]==10);
// The exact folder is sealed once. Repeated scans/discovery changes cannot retarget it.
Entity actor,folder;InventoryStorageManagerComponent inv;actor.inventory=&inv;inv.folders={&folder};
ERR_BodyEvidence record;record.Actor=&actor;service.record=&record;service.Capture(&actor);
assert(record.Issued.Count()==1);string target=record.Issued[0]->SiteId;
for(auto site:mode->locations)if(site)site->knowledge=2;
service.Capture(&actor);assert(record.Issued.Count()==1&&record.Issued[0]->SiteId==target);
assert(service.NextTarget().IsEmpty());
// Explicit mission clues survive even when no random unfinished target remains.
Entity objective;ERR_DocumentComponent doc;doc.target="radio";objective.document=&doc;
inv.folders.Insert(&objective);service.Capture(&actor);
assert(record.Issued.Count()==2&&record.Issued[1]->SiteId=="radio");
Entity extra;inv.folders.Insert(&extra);service.Capture(&actor);assert(record.Issued.Count()==2);
// Later availability can seal an unassigned folder, but not a spent/reported body's contents.
added.knowledge=0;record.Searched=true;service.Capture(&actor);assert(record.Issued.Count()==2);
record.Searched=false;actor.alive=false;service.Capture(&actor);assert(record.Issued.Count()==2);
actor.alive=true;service.Capture(&actor);assert(record.Issued.Count()==3&&record.Issued[2]->SiteId=="late-camp");
}
''')
print('PASS: 570 folders cover 57 unfinished sites evenly; known/automatic targets excluded, late camps included, and exact issued/authored documents preserved')