Files
EscapistsReintroduced/tests/test_mine_intel.py
T
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

97 lines
15 KiB
Python

"""Actual mine evidence/publication/marker bodies with native doubles, not engine/MP proof."""
import os,re
from pathlib import Path
from policy_harness import ROOT,body,run
P=Path(os.environ.get('ERR_MINE_INTEL_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/20_Intel/ERR_MineIntel.c'))
def cpp(s):
s=s.replace('protected ','').replace('ref ','').replace('out ','').replace('null','nullptr').replace('array<','Array<').replace(' = {};',';').replace('foreach (','for (')
s=s.replace('Array<IEntity> folders)', 'Array<IEntity>& folders)').replace('Array<IEntity> candidates)', 'Array<IEntity>& candidates)').replace('!candidates || ','')
for t in ['InventoryStorageManagerComponent','InventoryItemComponent','SCR_PressureTriggerComponent','ERR_MineIntelRecord','ERR_DiscoveryMarker','SCR_MapMarkerManagerComponent']:
s=re.sub(r'\b'+t+r' (\w+)',t+r'* \1',s)
for ns in ['Replication','ERR_GameMode','ERR_Actors','SCR_FactionManager','ERR_Resources','ERR_DiscoveryService','ERR_InfantryAmmoTransfer','ERR_ForceRules','ERR_EnemyLoadoutCatalog','ERR_DiscoveryMapRules','InventoryStorageManagerComponent','InventoryItemComponent','SCR_PressureTriggerComponent','SCR_MapMarkerManagerComponent','ERR_LocationKind','SCR_EMapMarkerType','vector']:
s=s.replace(ns+'.',ns+'::')
for obj in ['m_Mode','folder','actor','mine','player','inventory','item','trigger','record','known','manager','marker','m_ERRMineIntel']:
s=re.sub(r'\b'+obj+r'\.',obj+'->',s)
s=s.replace('bool revealed;', 'bool revealed=false;')
s=s.replace('GetPrefabData().','GetPrefabData()->').replace('Discovery().','Discovery()->').replace('Players().','Players()->').replace('GetGame().','GetGame()->').replace('GetCallqueue().','GetCallqueue()->').replace('OnStop().','OnStop()->')
s=re.sub(r'FindComponent\((\w+)\)',r'FindComponent("\1")',s)
s=s.replace('Remove(ERR_UpdateMineMarkers)','Remove(1)').replace('Remove(ERR_StopMineIntel)','Remove(2)')
return s
service='\n'.join(body(P,x) for x in ['protected bool Current(','protected bool Generic(','void Candidates(','bool Seal(','bool Recover(','void Stop('])
destructor=body(P,'void ~ERR_GameMode(')
mode='\n'.join(body(P,x) for x in ['bool ERR_RevealMineArea(','protected void ERR_ClearMineMarkers(','protected void ERR_UpdateMineMarkers(','protected void ERR_StopMineIntel('])
# Enforce references are not modelled by raw C++ pointers. Lifecycle bodies only.
run(r'''
#include <cassert>
#include <cmath>
#include <string>
#include <vector>
#include <algorithm>
struct vector {float a[3]={0,0,0};float& operator[](int n){return a[n];}bool operator==(const vector&b)const{return a[0]==b.a[0]&&a[1]==b.a[1]&&a[2]==b.a[2];}static float Distance(vector a,vector b){return std::sqrt(std::pow(a[0]-b[0],2)+std::pow(a[1]-b[1],2)+std::pow(a[2]-b[2],2));}};
vector Vector(float x,float y,float z){return {{x,y,z}};}
template<class T>struct Array:std::vector<T>{int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Clear(){this->clear();}bool Contains(T a){return std::find(this->begin(),this->end(),a)!=this->end();}void Insert(T a){this->push_back(a);}};
struct Entity;using IEntity=Entity*;struct Prefab {std::string name="intel";std::string GetPrefabName(){return name;}};
struct InventoryStorageManagerComponent {Array<IEntity> folders;static InventoryStorageManagerComponent*Cast(void*p){return (InventoryStorageManagerComponent*)p;}};
struct InventoryItemComponent {void*slot=nullptr;void*GetParentSlot(){return slot;}static InventoryItemComponent*Cast(void*p){return (InventoryItemComponent*)p;}};
struct SCR_PressureTriggerComponent {bool active=true;bool IsActivated(){return active;}static SCR_PressureTriggerComponent*Cast(void*p){return (SCR_PressureTriggerComponent*)p;}};
struct Entity {bool alive=true,player=false,objective=false;int faction=1,reads=0;IEntity owner=nullptr,parent=nullptr;vector position;Prefab prefab;InventoryStorageManagerComponent*inv=nullptr;InventoryItemComponent item;SCR_PressureTriggerComponent trigger;
Prefab*GetPrefabData(){reads++;return &prefab;}IEntity GetParent(){reads++;return parent;}vector GetOrigin(){reads++;return position;}
void*FindComponent(std::string t){reads++;if(t=="ERR_DocumentComponent")return objective?this:nullptr;if(t=="InventoryStorageManagerComponent")return inv;if(t=="InventoryItemComponent")return &item;if(t=="SCR_PressureTriggerComponent")return &trigger;return nullptr;}};
namespace Replication {bool server=true;int bumps=0;bool IsServer(){return server;}void BumpMe(){bumps++;}}
namespace ERR_Actors {bool CanFight(IEntity e){return e&&e->alive;}bool Alive(IEntity e){return e&&e->alive;}bool Player(IEntity e){return e&&e->player;}}
namespace SCR_FactionManager {int SGetFaction(IEntity e){return e?e->faction:0;}}
namespace ERR_Resources {std::string IntelFolder(){return "intel";}}
namespace ERR_EnemyLoadoutCatalog {std::string MINE="mine";}
namespace ERR_DiscoveryService {void Folders(InventoryStorageManagerComponent*i,Array<IEntity>&f){f=i->folders;}}
namespace ERR_InfantryAmmoTransfer {bool StoredUnder(IEntity f,IEntity a){return f&&a&&f->owner==a;}}
namespace ERR_ForceRules {bool FiniteVector(vector a){return std::isfinite(a[0])&&std::isfinite(a[1])&&std::isfinite(a[2]);}}
namespace ERR_DiscoveryMapRules {int Coordinate(float x,int){return std::floor(x/500.f)*500+250;}}
struct DiscoveryService {Array<IEntity> spent;bool IsSpent(IEntity f){return spent.Contains(f);}};
struct PlayersService {IEntity current=nullptr;bool CurrentActor(IEntity e){return e&&e==current;}};
struct Queue {int removed=0;void Remove(int){removed++;}};
struct Game {bool controller=true;Queue queue;bool GetPlayerController(){return controller;}Queue*GetCallqueue(){return &queue;}}game;Game*currentGame=&game;Game*GetGame(){return currentGame;}
struct Invoker {int removed=0;void Remove(int){removed++;}};
namespace ERR_LocationKind {int OUTPOST=3;}namespace SCR_EMapMarkerType {int PLACED_CUSTOM=1;}
struct ERR_DiscoveryMarker {vector position;std::string label;int deleted=0;void SetType(int){}void SetMarkerConfigID(int){}void SetDiscoveryPosition(vector p,int){position=p;}void SetLocationIcon(int,std::string,int){}void SetCustomText(std::string s){label=s;}void OnDelete(){deleted++;}};
struct SCR_MapMarkerManagerComponent {static inline SCR_MapMarkerManagerComponent*current=nullptr;bool config=true;Array<ERR_DiscoveryMarker*> inserted,removed;static SCR_MapMarkerManagerComponent*GetInstance(){return current;}bool GetMarkerConfig(){return config;}void ERR_InsertDiscoveryMarker(ERR_DiscoveryMarker*m){inserted.Insert(m);}void ERR_RemoveDiscoveryMarker(ERR_DiscoveryMarker*m){removed.Insert(m);m->OnDelete();}};
struct ERR_MineIntel;
struct ERR_GameMode {static inline ERR_GameMode*current=nullptr;bool active=true;int enemy=1,player=2;DiscoveryService discovery;PlayersService players;Invoker stop;
ERR_MineIntel*m_ERRMineIntel=nullptr;Array<vector>m_ERRMineAreas;Array<ERR_DiscoveryMarker*>m_ERRMineMarkers;bool m_ERRMineIntelStopped=false;SCR_MapMarkerManagerComponent*m_ERRMineMarkerOwner=nullptr;
static ERR_GameMode*Get(){return current;}bool Active(){return active;}int EnemyFaction(){return enemy;}int PlayerFaction(){return player;}DiscoveryService*Discovery(){return &discovery;}PlayersService*Players(){return &players;}Invoker*OnStop(){return &stop;}
void Destroy();bool ERR_RevealMineArea(vector);void ERR_ClearMineMarkers();void ERR_UpdateMineMarkers();void ERR_StopMineIntel();};
struct ERR_MineIntelRecord {IEntity Mine=nullptr;vector Area;Array<IEntity> Folders;bool Revealed=false;};
struct ERR_MineIntel {ERR_GameMode*m_Mode;Array<ERR_MineIntelRecord*>m_Records;int m_Bindings=0;bool m_Stopped=false;ERR_MineIntel(ERR_GameMode*m):m_Mode(m){}
'''+cpp(service)+r'''};
'''+re.sub(r'\b(bool|void) (ERR_\w+)\(',r'\1 ERR_GameMode::\2(',cpp(mode))+cpp(destructor).replace('void ~ERR_GameMode()', 'void ERR_GameMode::Destroy()')+r'''
struct Fixture {ERR_GameMode mode;ERR_MineIntel intel{&mode};Entity engineer,player,folder,mine;InventoryStorageManagerComponent ei,pi;Array<IEntity> candidates;
Fixture(){ERR_GameMode::current=&mode;Replication::server=true;currentGame=&game;game.controller=true;SCR_MapMarkerManagerComponent::current=nullptr;mode.m_ERRMineIntel=&intel;mode.players.current=&player;player.player=true;player.faction=2;engineer.inv=&ei;player.inv=&pi;folder.owner=&engineer;ei.folders.Insert(&folder);mine.prefab.name="mine";mine.position=Vector(1030,0,2340);}
void Capture(){intel.Candidates(&engineer,candidates);assert(candidates.Count()==1);}
void Seal(){Capture();assert(intel.Seal(&engineer,&mine,mine.position,candidates));assert(mode.m_ERRMineAreas.empty());}
void Transfer(){ei.folders.Clear();pi.folders.Insert(&folder);folder.owner=&player;}
};
int main(){
Fixture base;base.Seal();assert(!base.intel.Seal(&base.engineer,&base.mine,base.mine.position,base.candidates));base.Transfer();int reads=base.mine.reads;int actorReads=base.engineer.reads;base.mine.alive=false;base.mine.position=Vector(9000,0,9000);base.intel.Recover(&base.player,&base.folder);assert(base.mode.m_ERRMineAreas.Count()==1&&base.mode.m_ERRMineAreas[0]==Vector(1250,0,2250));assert(base.mine.reads==reads&&base.engineer.reads==actorReads);base.intel.Recover(&base.player,&base.folder);assert(base.mode.m_ERRMineAreas.Count()==1);
// Original site content is never modified or looked up by the evidence service.
// An objective, foreign/spent folder or a newly issued folder cannot be sealed.
for(int fault=0;fault<6;fault++){Fixture f;f.Capture();Entity newFolder;newFolder.owner=&f.engineer;
switch(fault){case 0:f.folder.objective=true;break;case 1:f.folder.owner=&f.player;break;case 2:f.mode.discovery.spent.Insert(&f.folder);break;case 3:f.ei.folders.Clear();f.ei.folders.Insert(&newFolder);break;case 4:f.engineer.faction=2;break;case 5:f.engineer.player=true;break;}
assert(!f.intel.Seal(&f.engineer,&f.mine,f.mine.position,f.candidates)&&f.intel.m_Records.empty()&&f.mode.m_ERRMineAreas.empty());}
// Unarmed, moved, destroyed, attached and unrelated mines cannot create records.
for(int fault=0;fault<5;fault++){Fixture f;f.Capture();vector position=f.mine.position;switch(fault){case 0:f.mine.trigger.active=false;break;case 1:f.mine.position=Vector(9999,0,0);break;case 2:f.mine.alive=false;break;case 3:f.mine.item.slot=&f.ei;break;case 4:f.mine.prefab.name="other";break;}assert(!f.intel.Seal(&f.engineer,&f.mine,position,f.candidates));}
for(int fault=0;fault<8;fault++){Fixture f;f.Seal();f.Transfer();ERR_GameMode newer;Entity other;switch(fault){case 0:f.mode.players.current=&other;break;case 1:f.player.alive=false;break;case 2:f.player.faction=1;break;case 3:f.folder.owner=&other;break;case 4:f.pi.folders.Clear();break;case 5:f.mode.discovery.spent.Insert(&f.folder);break;case 6:ERR_GameMode::current=&newer;break;case 7:f.intel.Stop();break;}f.intel.Recover(&f.player,&f.folder);assert(f.mode.m_ERRMineAreas.empty());}
Fixture caps;caps.Capture();caps.intel.m_Bindings=128;assert(!caps.intel.Seal(&caps.engineer,&caps.mine,caps.mine.position,caps.candidates));caps.intel.m_Bindings=0;caps.intel.m_Records.resize(32,nullptr);assert(!caps.intel.Seal(&caps.engineer,&caps.mine,caps.mine.position,caps.candidates));
Fixture publish;for(int i=0;i<32;i++)assert(publish.mode.ERR_RevealMineArea(Vector(i*500,0,0)));assert(publish.mode.ERR_RevealMineArea(Vector(10,0,20)));assert(!publish.mode.ERR_RevealMineArea(Vector(32000,0,0)));assert(!publish.mode.ERR_RevealMineArea(Vector(NAN,0,0)));
// Failed publication must remain recoverable instead of consuming warning.
Fixture retry;retry.Seal();retry.Transfer();for(int i=0;i<32;i++)retry.mode.m_ERRMineAreas.Insert(Vector(i*500+250,0,250));retry.intel.Recover(&retry.player,&retry.folder);assert(!retry.intel.m_Records[0]->Revealed);retry.mode.m_ERRMineAreas.Clear();retry.intel.Recover(&retry.player,&retry.folder);assert(retry.intel.m_Records[0]->Revealed);
// Marker polling waits through preparation/JIP and missing native dependencies.
Fixture markers;markers.mode.m_ERRMineAreas.Insert(Vector(1250,0,2250));markers.mode.active=false;markers.mode.ERR_UpdateMineMarkers();assert(!markers.mode.m_ERRMineIntelStopped);markers.mode.active=true;markers.mode.ERR_UpdateMineMarkers();assert(markers.mode.m_ERRMineMarkers.empty());SCR_MapMarkerManagerComponent first,second;SCR_MapMarkerManagerComponent::current=&first;first.config=false;markers.mode.ERR_UpdateMineMarkers();assert(first.inserted.empty());first.config=true;game.controller=false;markers.mode.ERR_UpdateMineMarkers();assert(first.inserted.empty());game.controller=true;markers.mode.ERR_UpdateMineMarkers();markers.mode.ERR_UpdateMineMarkers();assert(first.inserted.Count()==1&&first.inserted[0]->position==Vector(1250,0,2250));assert(first.inserted[0]->label=="Reported mine area (500 m); may still be active");
SCR_MapMarkerManagerComponent::current=&second;markers.mode.ERR_UpdateMineMarkers();assert(first.removed.Count()==1&&second.inserted.Count()==1);markers.mode.active=false;markers.mode.ERR_UpdateMineMarkers();assert(second.removed.Count()==1);markers.mode.active=true;markers.mode.ERR_UpdateMineMarkers();assert(second.inserted.Count()==2);markers.mode.ERR_StopMineIntel();markers.mode.ERR_StopMineIntel();assert(markers.intel.m_Stopped&&markers.mode.m_ERRMineMarkers.empty()&&markers.mode.m_ERRMineAreas.empty()&&second.removed.Count()==2);markers.mode.ERR_UpdateMineMarkers();assert(second.inserted.Count()==2);
// Shrink rebuild must retain manager ownership for replacement marker cleanup.
Fixture shrink;SCR_MapMarkerManagerComponent shrinking;SCR_MapMarkerManagerComponent::current=&shrinking;shrink.mode.m_ERRMineAreas.Insert(Vector(250,0,250));shrink.mode.m_ERRMineAreas.Insert(Vector(750,0,250));shrink.mode.ERR_UpdateMineMarkers();assert(shrinking.inserted.Count()==2);shrink.mode.m_ERRMineAreas.resize(1);shrink.mode.ERR_UpdateMineMarkers();assert(shrinking.removed.Count()==2&&shrinking.inserted.Count()==3&&shrink.mode.m_ERRMineMarkerOwner==&shrinking);shrink.mode.ERR_StopMineIntel();assert(shrinking.removed.Count()==3);
Fixture destroyed;SCR_MapMarkerManagerComponent deletion;SCR_MapMarkerManagerComponent::current=&deletion;destroyed.mode.m_ERRMineAreas.Insert(Vector(250,0,250));destroyed.mode.ERR_UpdateMineMarkers();int removedCallbacks=game.queue.removed;destroyed.mode.Destroy();assert(destroyed.intel.m_Stopped&&destroyed.mode.m_ERRMineMarkers.empty()&&deletion.removed.Count()==1&&game.queue.removed==removedCallbacks+1);
Fixture bindings;Array<Entity> moreFolders;moreFolders.resize(16);bindings.ei.folders.Clear();for(auto&f:moreFolders){f.owner=&bindings.engineer;bindings.ei.folders.Insert(&f);}bindings.intel.Candidates(&bindings.engineer,bindings.candidates);assert(bindings.candidates.Count()==16);Array<Entity> mines;mines.resize(9);for(int i=0;i<9;i++){mines[i].prefab.name="mine";mines[i].position=Vector(30,0,40);bool sealed=bindings.intel.Seal(&bindings.engineer,&mines[i],mines[i].position,bindings.candidates);assert(sealed==(i<8));}assert(bindings.intel.m_Bindings==128&&bindings.intel.m_Records.Count()==8);
Fixture replica;Replication::server=false;assert(!replica.mode.ERR_RevealMineArea(Vector(1000,0,2000)));replica.intel.Candidates(&replica.engineer,replica.candidates);assert(replica.candidates.empty());
}
''')
print('PASS: actual mine evidence sealing/recovery, historical coarse publication, caps, canonical ownership, marker join retry/owner-change/stop; native arming and replicated vector-array transport untested')