Require physical ammo access and prove finite native infantry transfer

This commit is contained in:
2026-09-20 09:22:52 +02:00
parent 5bd66d76ca
commit d8ec0de765
76 changed files with 14320 additions and 8 deletions
+74
View File
@@ -0,0 +1,74 @@
"""Execute actual authored storage-anchor selection and native trace guard."""
import os,re
from pathlib import Path
from policy_harness import ROOT,run
s=Path(os.environ.get('ERR_INFANTRY_ACCESS_SOURCE',ROOT/'addons/ai/Scripts/Game/ERR/ERR_InfantryAmmoAccess.c')).read_text()
s=re.sub(r'//[^\n]*','',s).replace('class ERR_InfantryAmmoAccess','struct ERR_InfantryAmmoAccess').replace('protected ','').replace('notnull ','')
s=s.replace('ActionsManagerComponent.Cast(donor.FindComponent(ActionsManagerComponent))','donor->manager').replace('SCR_OpenVehicleStorageAction.Cast(action)','dynamic_cast<SCR_OpenVehicleStorageAction*>(action)').replace('ChimeraCharacter.Cast(recipient)','dynamic_cast<Character*>(recipient)')
s=s.replace('array<UserActionContext>', 'array<UserActionContext*>').replace('array<BaseUserAction>', 'array<BaseUserAction*>')
s=s.replace('out array<vector> points','Array<vector>& points').replace('array<','Array<').replace('foreach (','for (')
for name in ('ActionsManagerComponent','UserActionContext','BaseUserAction','ChimeraCharacter','TraceParam','ERR_InfantryAmmoAccess'):
s=re.sub(r'\b'+name+r' (\w+)',name+r'* \1',s)
for name in ('manager','context','action','character','trace','access'):
s=s.replace(name+'.',name+'->')
for name in ('ERR_ConvoyRules','ERR_ForceRules','TraceFlags','vector'):
s=s.replace(name+'.',name+'::')
s=s.replace('GetGame().GetWorld()','GetGame()->GetWorld()').replace('GetWorld().','GetWorld()->').replace('null','nullptr')
s=s.replace('TraceMove(trace, Filter)','TraceMove(trace, [this](IEntity e){return Filter(e);})')
s=re.sub(r'\b(bool|IEntity) (\w+);',r'\1 \2{};',s).replace('\n}\n','\n};\n')
run(r'''
#include <cassert>
#include <vector>
#include <cmath>
#include <limits>
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;using std::vector<T>::operator=;int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Insert(T t){this->push_back(t);}};
struct vector{float x=0,y=0,z=0;float& operator[](int n){return n==0?x:n==1?y:z;}static float Distance(vector a,vector b){return std::sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));}};
struct Entity;struct Manager;using IEntity=Entity*;using Vehicle=Entity*;
struct Entity{virtual ~Entity(){}Manager*manager=nullptr;IEntity parent=nullptr;};
struct Character:Entity{vector aim={0,1.4,0};vector AimingPosition(){return aim;}};using ChimeraCharacter=Character;
struct BaseUserAction{virtual ~BaseUserAction(){}IEntity owner=nullptr;auto GetOwner(){return owner;}};
struct SCR_OpenVehicleStorageAction:BaseUserAction{};
struct UserActionContext{Array<BaseUserAction*>actions;vector anchor={2,1.4,0};bool valid=true;void GetActionsList(Array<BaseUserAction*>&a){a=actions;}bool GetTransformationWorld(vector (&mat)[4]){mat[3]=anchor;return valid;}};
struct Manager{bool enabled=true;Array<UserActionContext*>contexts;bool IsEnabled(){return enabled;}void GetContextList(Array<UserActionContext*>&a){a=contexts;}};using ActionsManagerComponent=Manager;
struct TraceFlags{static constexpr int WORLD=1,ENTS=2;};static constexpr int TRACE_LAYER_CAMERA=99;
struct TraceParam{vector Start,End;int Flags=0,LayerMask=0;};
struct World{float fraction=1;Array<IEntity>obstacles;int traces=0;vector lastStart,lastEnd;
template<class F>float TraceMove(TraceParam*p,F filter){traces++;lastStart=p->Start;lastEnd=p->End;assert(p->Flags==3&&p->LayerMask==99);for(auto entity:obstacles)if(filter(entity))return .5;return fraction;}};
struct Game{World world;auto GetWorld(){return &world;}} game;Game*GetGame(){return &game;}
struct ERR_ConvoyRules{static bool PartOf(IEntity a,IEntity root){int n=0;while(a&&n++<32){if(a==root)return true;a=a->parent;}return false;}};
struct ERR_ForceRules{static bool FiniteVector(vector v){return std::isfinite(v.x)&&std::isfinite(v.y)&&std::isfinite(v.z);}};
''' + s + r'''
int main(){
Entity truck,truckPart,gear,wall,otherTruck,otherChild;Character actor;truckPart.parent=&truck;gear.parent=&actor;otherChild.parent=&otherTruck;
Manager manager;truck.manager=&manager;UserActionContext context;SCR_OpenVehicleStorageAction storage;storage.owner=&truck;context.actions={&storage};manager.contexts={&context};
Array<vector>points;assert(ERR_InfantryAmmoAccess::Points(&truck,points)&&points.Count()==1);
ERR_InfantryAmmoAccess access;assert(access.Check(&truck,&actor));assert(game.world.lastStart.y==1.4f&&game.world.lastEnd.x==2);
// Own hull/gear cannot falsely block an interior-authored native storage anchor.
game.world.obstacles={&truck,&truckPart,&actor,&gear};assert(access.Check(&truck,&actor));
// A wall or another vehicle still blocks even when owned geometry preceded it.
game.world.obstacles.push_back(&wall);assert(!access.Check(&truck,&actor));
game.world.obstacles={&truck,&otherChild};assert(!access.Check(&truck,&actor));game.world.obstacles.clear();
for(float value:{0.f,.99f,-1.f,2.f,std::numeric_limits<float>::quiet_NaN()}){game.world.fraction=value;assert(!access.Check(&truck,&actor));}game.world.fraction=1;
context.anchor.x=4.01;assert(!access.Check(&truck,&actor));context.anchor.x=0;assert(!access.Check(&truck,&actor));context.anchor.x=2;
actor.aim.y=std::numeric_limits<float>::quiet_NaN();assert(!access.Check(&truck,&actor));actor.aim.y=1.4;
context.valid=false;assert(!access.Check(&truck,&actor));context.valid=true;
context.anchor.z=std::numeric_limits<float>::quiet_NaN();assert(!access.Check(&truck,&actor));context.anchor.z=0;
storage.owner=&otherTruck;assert(!access.Check(&truck,&actor));storage.owner=&truck;
BaseUserAction door;door.owner=&truck;context.actions={&door};assert(!access.Check(&truck,&actor));context.actions={&storage};
manager.enabled=false;assert(!access.Check(&truck,&actor));manager.enabled=true;
// Stock Ural defines 36 root contexts and ammo adds one: a valid storage action near the end must remain discoverable.
for(int count:{37,64}){
std::vector<UserActionContext> stockContexts(count);manager.contexts.clear();
for(auto& authored:stockContexts){authored.actions={&door};manager.contexts.push_back(&authored);}
stockContexts.back().actions={&storage};
assert(ERR_InfantryAmmoAccess::Points(&truck,points)&&points.Count()==1&&access.Check(&truck,&actor));
}
manager.contexts.clear();manager.contexts.resize(65,&context);assert(!access.Check(&truck,&actor));manager.contexts={&context};
context.actions.resize(17,&storage);assert(!access.Check(&truck,&actor));context.actions={&storage};
manager.contexts={nullptr};assert(!access.Check(&truck,&actor));manager.contexts={&context};
assert(!access.Check(&truck,&wall));assert(!access.Check(nullptr,&actor));
// A blocked context never skips an intervening external entity; another independently clear authored context may qualify.
UserActionContext distant;distant.actions={&storage};distant.anchor={9,1.4,0};manager.contexts={&distant,&context};assert(access.Check(&truck,&actor));
}
''')
print('PASS: actual authored storage anchors, native trace flags, own-hierarchy exclusion, external wall/vehicle obstruction, range, unknown geometry and bounded context refusal')
+10 -1
View File
@@ -22,7 +22,7 @@ for src,dst in {
code=code.replace('ref ERR_InfantryAmmoCallback','ERR_InfantryAmmoCallback*').replace('array<','Array<').replace('foreach (','for (').replace('null','nullptr')
code=re.sub(r'\bERR_GameMode (?=\w)','ERR_GameMode* ',code)
code=code.replace('void ERR_InfantryAmmoTransfer(','ERR_InfantryAmmoTransfer(').replace('EWeaponType kind','int kind').replace('EMuzzleType muzzleKind','int muzzleKind')
for name in ('ERR_InfantryAmmoTransferState','ERR_GameMode','ERR_EnemyEquipmentRules','ERR_ForceRules','Replication','SCR_Global','ERR_Actors','SCR_FactionManager','ERR_ConvoyRules','CompartmentAccessComponent','EWeaponType','EMuzzleType','EStoragePurpose','vector'):
for name in ('ERR_InfantryAmmoTransferState','ERR_InfantryAmmoAccess','ERR_GameMode','ERR_EnemyEquipmentRules','ERR_ForceRules','Replication','SCR_Global','ERR_Actors','SCR_FactionManager','ERR_ConvoyRules','CompartmentAccessComponent','EWeaponType','EMuzzleType','EStoragePurpose','vector'):
code=code.replace(name+'.',name+'::')
for name in ('m_Mode','m_Donor','m_DonorGroup','m_Recipient','m_RecipientGroup','m_Source','m_Destination','m_Storage','m_Callback','magazine','manager','weapon','muzzle','entity','item','storage','inventory','donorRecord','recipientRecord','utility','usage','physics'):
code=re.sub(r'\b'+name+r'\.',name+'->',code)
@@ -82,6 +82,7 @@ struct CompartmentAccessComponent{static IEntity GetVehicleIn(IEntity a){return
struct ERR_ConvoyRules{static bool Captured(Vehicle v,SCR_AIGroup g){return v->captured||g->players>0;}static bool PartOf(IEntity a,IEntity root){return a==root;}};
struct ERR_EnemyEquipmentRules{static bool Eligible(ERR_GameMode*m,SCR_AIGroup g,IEntity a){return m&&g&&a&&a->alive&&a->awake&&!a->player&&a->group==g&&g->players==0&&g->faction==m->EnemyFaction()&&g->utility&&g->utility->military;}};
struct ERR_ForceRules{static bool Finite(float n){return std::isfinite(n);}};
struct ERR_InfantryAmmoAccess{static inline bool clear=true;static inline int checks=0;static bool Clear(Vehicle donor,IEntity recipient){assert(donor&&recipient);checks++;return clear;}};
struct Inventory{IEntity owner=nullptr;Array<IEntity>items;Array<Storage*>storages;Storage*selected=nullptr;bool allow=true,accept=true;int calls=0,callback=0;ScriptedInventoryOperationCallback*last=nullptr;
IEntity GetOwner(){return owner;}void GetItems(Array<IEntity>&a,int purpose){assert(purpose==1);a=items;}void GetStorages(Array<Storage*>&a,int purpose){assert(purpose==1);a=storages;}
Storage*FindStorageForItem(IEntity,int purpose){assert(purpose==1);return selected;}
@@ -102,6 +103,14 @@ struct Fixture{ERR_GameMode mode;Ledger ledger;Group donorGroup,recipientGroup,o
void Move(){source.items.clear();destination.items={&magEntity};item.slot=&toSlot;}
};
int main(){
// A blocked native access check happens before submission and is terminal.
{Fixture f;auto t=f.Job();ERR_InfantryAmmoAccess::clear=false;ERR_InfantryAmmoAccess::checks=0;
assert(t.Tick(0)==FAILED&&!t.Submitted()&&!t.Unresolved()&&f.source.calls==0&&ERR_InfantryAmmoAccess::checks==1);
ERR_InfantryAmmoAccess::clear=true;assert(t.Tick(1)==FAILED&&f.source.calls==0&&ERR_InfantryAmmoAccess::checks==1);}
// Once native move is accepted, later geometry cannot cause a second operation or pretend cancellation.
{Fixture f;auto t=f.Job();ERR_InfantryAmmoAccess::checks=0;assert(t.Tick(0)==PENDING&&ERR_InfantryAmmoAccess::checks==1);
ERR_InfantryAmmoAccess::clear=false;assert(t.Tick(1)==PENDING&&t.Unresolved()&&f.source.calls==1&&ERR_InfantryAmmoAccess::checks==1);
f.Move();f.source.last->OnComplete();assert(t.Tick(2)==SUCCEEDED);ERR_InfantryAmmoAccess::clear=true;}
// Read-only selection follows the native inherited-well direction, excluding launcher muzzles and weapons.
{Fixture f;assert(ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));Well derived;derived.type=2;derived.parent=1;f.magazine.well=&derived;assert(ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));
f.magazine.well=&f.well;f.muzzle.well=&derived;assert(!ERR_InfantryAmmoTransfer::Compatible(&f.recipient,&f.magEntity));
+77
View File
@@ -0,0 +1,77 @@
"""Execute actual native-fixture Place/Stop: delayed pose observation and cleanup."""
import os,re
from pathlib import Path
from policy_harness import ROOT,body,run
p=Path(os.environ.get('ERR_INFANTRY_PROBE_SOURCE',ROOT/'addons/chat/Scripts/Game/ERR/ERR_NativeInfantryTransferProbe.c'))
s=body(p,'protected int Place(float now)')+'\n'+body(p,' void Stop()')
s=re.sub(r'//[^\n]*','',s).replace('protected ','')
s=s.replace('ChimeraCharacter.Cast(m_Actor)','m_Actor')
s=re.sub(r'"(-?\d+(?:\.\d+)?) (-?\d+(?:\.\d+)?) (-?\d+(?:\.\d+)?)"',r'vector{\1,\2,\3}',s)
s=s.replace('array<','Array<').replace('TraceBox body','TraceBox* body').replace('ChimeraCharacter character','Entity* character')
for name in ('m_Truck','m_Actor','character','body','m_Move','m_HQ','m_Unit'):
s=s.replace(name+'.',name+'->')
s=s.replace('Entity.GetTransform','Entity->GetTransform').replace('Entity.GetOrigin','Entity->GetOrigin')
for name in ('ERR_Actors','ERR_InfantryAmmoAccess','TraceFlags'):
s=s.replace(name+'.',name+'::')
s=s.replace('GetGame().GetWorld().','GetGame()->GetWorld()->').replace('null','nullptr')
run(r'''
#include <cassert>
#include <vector>
#include <cmath>
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}};
struct vector{float x=0,y=0,z=0;float operator[](int i){return i==0?x:i==1?y:z;}vector operator+(vector b){return {x+b.x,y+b.y,z+b.z};}vector operator*(float f){return {x*f,y*f,z*f};}bool operator==(vector b){return x==b.x&&y==b.y&&z==b.z;}};
struct Entity{vector origin,aim;int moves=0;vector GetOrigin(){return origin;}vector AimingPosition(){return aim;}void SetOrigin(vector v){origin=v;moves++;}void GetTransform(vector (&out)[4]){out[0]={1,0,0};out[1]={0,1,0};out[2]={0,0,1};out[3]=origin;}void EnginePoseUpdate(){aim=origin+vector{0,1.4f,0};}};
using IEntity=Entity*;
struct TraceFlags{static constexpr int ENTS=2;};
struct TraceBox{vector Start,End,Mins,Maxs;int Flags=0;Array<IEntity>ExcludeArray;};
struct World{int traces=0;float fraction=1;float TraceMove(TraceBox*b,void*){traces++;assert(b->Flags==2&&b->ExcludeArray.Count()==1);return fraction;}};
struct Game{World world;World*GetWorld(){return &world;}} game;Game*GetGame(){return &game;}
struct ERR_Actors{static vector Ground(vector p){p.y=0;return p;}};
struct ERR_InfantryAmmoAccess{static inline bool points=true,clear=true;static inline int clearCalls=0;
static bool Points(IEntity,Array<vector>& out){out={{1,1,1}};return points;}
static bool Clear(IEntity,IEntity actor){clearCalls++;return clear&&actor->aim==actor->origin+vector{0,1.4f,0};}};
template<class... Args>void PrintFormat(const char*,Args...){}
struct Truck{IEntity Entity=nullptr;int SupplyToken=200001;bool SupplyHeld=true;};
struct Unit{int Group=1;};
struct HQ{int token=200001,releases=0;int LogisticsToken(int){return token;}void ReleaseLogistics(int,int expected){if(token!=expected)return;token=0;releases++;}};
struct Move{bool unresolved=false;int stops=0;void Stop(){stops++;}bool Unresolved(){return unresolved;}};
struct Probe{static constexpr int TOKEN=200001;Truck*m_Truck=nullptr;IEntity m_Actor=nullptr;HQ*m_HQ=nullptr;Unit*m_Unit=nullptr;Move*m_Move=nullptr;
bool m_Positioned=false,m_PlacementStarted=false,m_PlacementPending=false,m_Stopped=false,owned=true;
int m_PlacementIndex=0,diagnostics=0;float m_PlacementReadyAt=0;vector m_OriginalPosition;
bool RecipientOwned(){return owned;}void AccessDiagnostic(){diagnostics++;}
''' + s + r'''
};
struct Fixture{Probe p;Truck truck;Entity vehicle,actor;HQ hq;Unit unit;Move move;
Fixture(){game.world=World{};ERR_InfantryAmmoAccess::points=true;ERR_InfantryAmmoAccess::clear=true;ERR_InfantryAmmoAccess::clearCalls=0;
vehicle.origin={100,0,100};actor.origin={900,0,900};actor.EnginePoseUpdate();truck.Entity=&vehicle;p.m_Truck=&truck;p.m_Actor=&actor;p.m_HQ=&hq;p.m_Unit=&unit;}
};
int main(){
// SetOrigin deliberately leaves the native aim pose stale. Same-tick calls must not inspect it.
{Fixture f;auto original=f.actor.origin;assert(f.p.Place(0)==0);assert(f.actor.moves==1&&f.p.m_PlacementPending&&f.p.m_Positioned);
assert(f.actor.aim==original+vector({0,1.4f,0}));assert(ERR_InfantryAmmoAccess::clearCalls==0);
assert(f.p.Place(249)==0&&f.actor.moves==1&&ERR_InfantryAmmoAccess::clearCalls==0);
f.actor.EnginePoseUpdate();assert(f.p.Place(250)==1&&ERR_InfantryAmmoAccess::clearCalls==1);assert(f.p.m_OriginalPosition==original);
f.p.Stop();assert(f.actor.origin==original&&f.actor.moves==2&&f.hq.releases==1&&!f.truck.SupplyHeld&&f.truck.SupplyToken==0);
f.p.Stop();assert(f.actor.moves==2&&f.hq.releases==1);}
// Every controlled candidate is bounded; failed checks never overwrite the true original position.
{Fixture f;auto original=f.actor.origin;ERR_InfantryAmmoAccess::clear=false;
for(int i=0;i<4;i++){assert(f.p.Place(i*250)==0);assert(f.p.m_PlacementIndex==i+1&&f.p.m_OriginalPosition==original);f.actor.EnginePoseUpdate();}
assert(f.p.Place(1000)==-1&&f.p.m_PlacementIndex==4&&ERR_InfantryAmmoAccess::clearCalls==4&&game.world.traces==4);
assert(f.actor.origin==original);int before=f.actor.moves;assert(f.p.Place(1250)==-1&&f.actor.moves==before&&game.world.traces==4);f.p.Stop();assert(f.actor.origin==original);}
// Interrupted async positioning restores its original pose even before a successful access observation.
{Fixture f;auto original=f.actor.origin;assert(f.p.Place(0)==0);f.p.Stop();assert(f.actor.origin==original&&f.hq.releases==1&&!f.truck.SupplyHeld);}
// Geometry refusal tries at most four candidates, never moves the actor and never invokes access rays.
{Fixture f;auto original=f.actor.origin;game.world.fraction=.5;assert(f.p.Place(0)==-1&&game.world.traces==4&&f.actor.moves==0&&ERR_InfantryAmmoAccess::clearCalls==0);
f.p.Stop();assert(f.actor.origin==original&&f.actor.moves==0);}
// Unknown authored anchor cannot silently fall back to fixture coordinates.
{Fixture f;ERR_InfantryAmmoAccess::points=false;assert(f.p.Place(0)==-1&&f.p.diagnostics==1&&f.actor.moves==0&&game.world.traces==0);}
// Do not race an accepted uncertain inventory move with a cleanup teleport.
{Fixture f;assert(f.p.Place(0)==0);auto current=f.actor.origin;f.p.m_Move=&f.move;f.move.unresolved=true;f.p.Stop();assert(f.actor.origin==current&&f.move.stops==1&&f.hq.releases==1);}
// Reassigned/captured actor ownership prevents placement and cleanup mutation.
{Fixture f;f.p.owned=false;assert(f.p.Place(0)==-1&&f.actor.moves==0);}
{Fixture f;assert(f.p.Place(0)==0);auto current=f.actor.origin;f.p.owned=false;assert(f.p.Place(250)==-1);f.p.Stop();assert(f.actor.origin==current&&f.actor.moves==1);}
// Lost HQ reservation similarly cannot authorize cleanup teleport.
{Fixture f;assert(f.p.Place(0)==0);auto current=f.actor.origin;f.hq.token=91;f.p.Stop();assert(f.actor.origin==current&&f.hq.releases==0);}
}
''')
print('PASS: actual Place/Stop waits native pose update, bounds four attempts, preserves original position, restores pending/settled placement and respects ownership/accepted-move quarantine')
+141
View File
@@ -0,0 +1,141 @@
"""Execute real finite-stock/planning/reservation bodies; native inventory/group APIs are doubles."""
import os,re
from pathlib import Path
from policy_harness import ROOT,body,run
P=Path(os.environ.get('ERR_INFANTRY_RESUPPLY_SOURCE',ROOT/'addons/ai/Scripts/Game/ERR/ERR_InfantryResupply.c'))
I=ROOT/'addons/core/Scripts/Game/ERR/ERR_Inventory.c'
H=ROOT/'addons/ai/Scripts/Game/ERR/ERR_HQ.c'
def cpp(s):
s=s.replace('protected ','').replace('notnull ','').replace('null','nullptr')
s=s.replace('array<IEntity> items = {};','Array<IEntity> items;').replace('array<IEntity> raw = {};','Array<IEntity> raw;').replace('array<AIAgent> agents = {};','Array<AIAgent> agents;')
s=s.replace('out array<IEntity> items','Array<IEntity>& items').replace('array<IEntity> items','Array<IEntity>& items')
s=re.sub(r'foreach \((\w+) (\w+) : ([^)]+)\)',r'for (\1 \2 : \3)',s)
for typ in ('InventoryStorageManagerComponent','ERR_ForceRecord'):
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
for name in ('ERR_ForceRules','ERR_InfantryResupplyRules','ERR_ConvoyRules','ERR_ForceKind','ERR_ConvoyResources','ERR_Inventory','ERR_Actors','SCR_FactionManager','CompartmentAccessComponent','Replication','Math'):
s=s.replace(name+'.',name+'::')
s=s.replace('vector.DistanceXZ','Vec::DistanceXZ').replace('InventoryStorageManagerComponent.Cast','InventoryStorageManagerComponent::Cast').replace('FindComponent(InventoryStorageManagerComponent)','FindComponent()')
for name in ('m_Mode','m_HQ','m_Convoy','m_Truck','m_Recipient','m_Donor','m_Move','recipient','donor','demand','donorStatus','entry','operation','inventory','item','owner','record','actor','agent','unit','m_MoveWaypoint','m_HoldWaypoint','magazine'):
s=s.replace(name+'.',name+'->')
for name in ('Receipt','Entity','Group','Utility','StatusSampler','AmmoState','Combat','m_Perception'):
s=s.replace(name+'.',name+'->')
for name in ('Forces()','GetPrefabData()'):s=s.replace(name+'.',name+'->')
s=s.replace('bool available;','bool available=false;').replace('bool living;','bool living=false;')
return s
rules='\n'.join(cpp(body(P,x)) for x in ('static bool Fresh(', 'static bool Need(', 'static bool Pair('))
methods='\n'.join(cpp(body(P,x)) for x in ('protected bool Contains(', 'protected bool TruckValid(', 'protected bool Quiet(', 'protected bool Roster(', 'protected void Stock(', 'bool TryPlan(', 'protected void ClearWaypoints(', 'protected void Finish(', 'void Stop('))
reserve=cpp(body(H,'bool ReserveLogistics('))+cpp(body(H,'void ReleaseLogistics('))
unique=cpp(body(I,'static int AppendUnique('))+cpp(body(I,'static int Items('))
run(r'''
#include <cassert>
#include <string>
#include <vector>
#include <array>
#include <map>
#include <algorithm>
#include <cmath>
using string=std::string;
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Clear(){this->clear();}void Insert(T v){this->push_back(v);}bool Contains(T v){return std::find(this->begin(),this->end(),v)!=this->end();}};
template<class V>struct Map{std::map<string,V>data;V Get(string k){auto it=data.find(k);return it==data.end()?V{}:it->second;}void Set(string k,V v){data[k]=v;}};
struct Vec{float x=0,y=0,z=0;static float DistanceXZ(Vec a,Vec b){return std::hypot(a.x-b.x,a.z-b.z);}};
struct ERR_ForceRules{static bool Finite(float f){return std::isfinite(f);}static bool FiniteVector(Vec v){return Finite(v.x)&&Finite(v.y)&&Finite(v.z);}};
struct Math{static float Min(float a,float b){return std::min(a,b);}};
struct Replication{static inline bool server=true;static bool IsServer(){return server;}};
struct Entity;using IEntity=Entity*;
struct InventoryStorageManagerComponent{Array<IEntity>items;void GetItems(Array<IEntity>& out){out=items;}static auto Cast(InventoryStorageManagerComponent* p){return p;}};
struct Prefab{string name="magazine";string GetPrefabName(){return name;}};
struct Group;using SCR_AIGroup=Group*;
struct Entity{Prefab prefab;bool hasPrefab=true,alive=true,player=false,fight=true,aboard=false,captured=false,released=false;int faction=1;Group* group=nullptr;InventoryStorageManagerComponent* inventory=nullptr;Prefab* GetPrefabData(){return hasPrefab?&prefab:nullptr;}InventoryStorageManagerComponent* FindComponent(){return inventory;}};
struct Agent{IEntity actor;IEntity GetControlledEntity(){return actor;}};using AIAgent=Agent*;
struct Waypoint{};
struct Group{Array<AIAgent>agents;bool slave=false;int players=0,faction=1;bool IsSlave(){return slave;}int GetPlayerCount(bool){return players;}int GetFaction(){return faction;}void GetAgents(Array<AIAgent>& out){out=agents;}void RemoveWaypoint(Waypoint*){}};
struct ERR_Actors{static bool Alive(IEntity a){return a&&a->alive;}static bool Player(IEntity a){return a&&a->player;}static SCR_AIGroup Group(IEntity a){return a->group;}static bool CanFight(IEntity a){return a&&a->alive&&a->fight;}static void DeleteSpawned(Waypoint*){}};
struct SCR_FactionManager{static int SGetFaction(IEntity a){return a->faction;}};
struct CompartmentAccessComponent{static bool GetVehicleIn(IEntity a){return a->aboard;}};
struct ERR_Inventory{
'''+unique+r'''
};
struct Ammo{bool Complete=true;int Members=2,SmallArmsMuzzles=2,SmallArmsRounds=60,SmallArmsChambers=0;bool Valid(int n){return Members==n;}};
struct Entry{bool Closed=false;int Count=2,Fit=2,MessageId=1;float ObservedAt=0;Vec Position;string SourceId;Ammo* AmmoState=nullptr;};using ERR_HQWatchEntry=Entry*;
struct ERR_InfantryResupplyRules{
'''+rules+r'''
};
namespace ERR_ForceKind{const int CONVOY=3;}
struct ERR_ForceRecord{bool Physical=true,Eliminated=false;int Kind=3,Id=2;};
struct Ledger{Group* owned=nullptr;ERR_ForceRecord record;ERR_ForceRecord* FindGroup(Group* g){return g==owned?&record:nullptr;}};
struct Mode{Ledger ledger;Ledger* Forces(){return &ledger;}int EnemyFaction(){return 1;}};
struct Perception{Array<int>m_aTargets;};
struct Utility{Perception* m_Perception=nullptr;};
struct Sampler{Group* group;Array<IEntity>original;int InitialStrength(){return original.Count();}IEntity InitialMember(int i){return original[i];}bool CanAttribute(int i,IEntity a){return original[i]==a&&!a->released;}bool OwnsRoster(Group* g){return g==group;}};
struct Unit{struct Group* Group=nullptr;struct Utility* Utility=nullptr;Sampler* StatusSampler=nullptr;string ForceSourceId;bool HomeGuard=false,eligible=true;int LogisticsReservation=0;void* Downlink=nullptr;void* PendingOrder=nullptr;void* PendingConvoyOrder=nullptr;void* Radio=this;void* Assignment=nullptr;void* Waypoint=nullptr;bool Eligible(Mode*){return eligible&&LogisticsReservation==0;}};using ERR_HQUnit=Unit*;
struct Combat{bool Held=false;};
struct Truck{int Index=1,SupplyToken=0;bool Registered=true,Released=false,Arrived=false,SupplyHeld=false,crew=true;struct Entity* Entity=nullptr;struct Group* Group=nullptr;struct Combat* Combat=nullptr;bool CrewValid(Mode*){return crew;}};
struct Receipt{bool Consumed=true;Array<int>Records={1,2,3,4,5,6};};
struct Cargo{bool Finished=true;};using ERR_ConvoyCargoCallback=Cargo*;
struct Convoy{bool Stopped=false,Ready=true;struct Receipt* Receipt=nullptr;Array<Truck*>Vehicles;Array<struct Cargo*>Cargo;float Deadline=30000;};
struct ERR_ConvoyRules{static bool Captured(Entity* e,Group*){return e->captured;}};
struct ERR_ConvoyResources{static string Magazine(){return "magazine";}};
struct Route{};struct Leaf{void Stop(){}};
struct HQ{Mode* m_Mode;Array<ERR_HQUnit>m_Units;Convoy* convoy=nullptr;Route route;bool active=true,routeAvailable=true;int downlinks=0;bool MissionActive(){return active;}Convoy* ConvoyTask(){return convoy;}
Route* Downlink(Group*,string,string,int){downlinks++;return routeAvailable?&route:nullptr;}
'''+reserve+r'''
};
struct Service{HQ* m_HQ;Mode* m_Mode;Convoy* m_Convoy=nullptr;Truck* m_Truck=nullptr;Unit* m_Recipient=nullptr;Unit* m_Donor=nullptr;Leaf* m_Move=nullptr;Route* m_Order=nullptr;IEntity m_Receiving=nullptr;
Array<IEntity>m_Stock,m_Spent,m_Members,m_Served;Map<int>m_Consumed;Map<float>m_NextAttempt;int m_State=0,m_Trip=0,m_Token=0,m_ReportId=0,m_TripDelivered=0;float m_Deadline=0;bool m_Stopped=false;
Waypoint* m_MoveWaypoint=nullptr;Waypoint* m_HoldWaypoint=nullptr;string reason;bool Current(){return !m_Stopped&&Replication::IsServer()&&m_HQ->active;}void Audit(string why){reason=why;}
'''+methods+r'''
};
struct Fixture{Mode mode;HQ hq;Service s;InventoryStorageManagerComponent inventory;Entity vehicle;std::array<Entity,140>items;Entity one,two;Agent a{&one},b{&two};Group recipients,donors;Utility recipientUtility,donorUtility;Sampler sampler;Unit recipient,donor;Combat combat;Truck truck;Receipt receipt;Cargo cargo;Convoy convoy;Ammo ammo;Entry demand,donorStatus;
Fixture():sampler{&recipients,{&one,&two}}{
hq.m_Mode=&mode;hq.convoy=&convoy;s.m_HQ=&hq;s.m_Mode=&mode;
vehicle.inventory=&inventory;vehicle.group=&donors;truck.Entity=&vehicle;truck.Group=&donors;truck.Combat=&combat;
mode.ledger.owned=&donors;convoy.Receipt=&receipt;convoy.Vehicles={&truck,&truck,&truck,&truck,&truck,&truck};convoy.Cargo={&cargo};
one.group=&recipients;two.group=&recipients;recipients.agents={&a,&b};recipient.Group=&recipients;recipient.Utility=&recipientUtility;recipient.StatusSampler=&sampler;recipient.ForceSourceId="recipient";
donor.Group=&donors;donor.Utility=&donorUtility;donor.ForceSourceId="donor";hq.m_Units={&recipient,&donor};
demand.SourceId="recipient";demand.AmmoState=&ammo;donorStatus.SourceId="donor";donorStatus.AmmoState=&ammo;
for(int i=0;i<48;i++)inventory.items.Insert(&items[i]);
}
bool Plan(float now=0){return s.TryPlan(&recipient,&donor,&demand,&donorStatus,now);}
void Refresh(float now,int id){demand.ObservedAt=now;donorStatus.ObservedAt=now;demand.MessageId=id;}
void Unchanged(){assert(s.m_Trip==0&&s.m_Consumed.Get("recipient")==0&&s.m_NextAttempt.Get("recipient")==0&&recipient.LogisticsReservation==0&&truck.SupplyToken==0&&hq.downlinks==0);}
};
int main(){
// Actual inventory aggregation collapses repeated native storage references, not distinct identical-prefab magazines.
{Fixture f;f.inventory.items.Insert(&f.items[0]);f.inventory.items.Insert(nullptr);f.s.Stock(0);assert(f.s.m_State==1&&!f.s.m_Stopped&&f.s.m_Stock.Count()==48);assert(f.s.m_Stock[0]!=f.s.m_Stock[1]);}
// Malformed/non-magazine items never become spendable stock; native startup partial cargo stays partial.
{Fixture f;f.inventory.items={&f.items[0],&f.items[1],&f.items[2],nullptr};f.items[1].hasPrefab=false;f.items[2].prefab.name="rifle";f.s.Stock(0);assert(f.s.m_Stock.Count()==1&&f.s.m_State==1);}
{Fixture f;f.cargo.Finished=false;f.s.Stock(0);assert(f.s.m_State==0&&f.s.m_Stock.IsEmpty());f.s.Stock(30000);assert(f.s.m_Stopped&&f.s.m_Stock.IsEmpty());}
{Fixture f;f.cargo.Finished=false;f.s.Stock(0);f.cargo.Finished=true;f.inventory.items={&f.items[0]};f.s.Stock(1000);assert(f.s.m_Stock.Count()==1&&!f.s.m_Stopped);}
{Fixture f;f.inventory.items.Insert(&f.items[48]);f.s.Stock(0);assert(f.s.m_Stopped&&!f.Plan());f.Unchanged();}
{Fixture f;for(auto& item:f.items)f.inventory.items.Insert(&item);f.s.Stock(0);assert(f.s.m_Stopped&&f.s.m_Stock.IsEmpty());}
{Fixture f;f.vehicle.inventory=nullptr;f.s.Stock(0);assert(f.s.m_Stopped&&!f.Plan());}
{Fixture f;f.convoy.Ready=false;f.s.Stock(0);assert(f.s.m_State==0&&f.s.m_Stock.IsEmpty());}
{Fixture f;f.receipt.Consumed=false;f.s.Stock(0);assert(f.s.m_Stopped&&f.s.m_Stock.IsEmpty());}
// Removed stock is unavailable; a different magazine added after capture does not replace its identity.
{Fixture f;f.s.Stock(0);f.inventory.items={&f.items[48]};assert(!f.Plan());f.Unchanged();f.inventory.items.Insert(&f.items[0]);assert(f.Plan());assert(f.s.m_Stock.Count()==48&&!f.s.m_Stock.Contains(&f.items[48]));}
{Fixture f;f.s.Stock(0);f.s.m_Spent=f.s.m_Stock;assert(!f.Plan());f.Unchanged();}
// A delivered report is tied to exact original source identities and original still-owned members.
{Fixture f;f.s.Stock(0);f.demand.SourceId="foreign";assert(!f.Plan());f.Unchanged();}
{Fixture f;f.s.Stock(0);f.donorStatus.SourceId="foreign";assert(!f.Plan());f.Unchanged();}
{Fixture f;f.s.Stock(0);f.one.released=true;assert(!f.Plan());f.Unchanged();assert(!f.s.m_Recipient&&!f.s.m_Donor);}
{Fixture f;f.s.Stock(0);Entity foreign;foreign.group=&f.recipients;Agent extra{&foreign};f.recipients.agents.Insert(&extra);assert(!f.Plan());f.Unchanged();}
// Real HQ force reservation refuses busy/support-ineligible or silent-radio recipients without consuming the demand.
for(int refusal=0;refusal<5;refusal++){Fixture f;f.s.Stock(0);void* owned=&f;
if(refusal==0){f.recipient.Assignment=owned;}if(refusal==1){f.recipient.Downlink=owned;}if(refusal==2){f.recipient.Waypoint=owned;}if(refusal==3){f.recipient.Radio=nullptr;}if(refusal==4){f.recipient.eligible=false;}
assert(!f.Plan());f.Unchanged();assert(!f.s.m_Recipient&&!f.s.m_Donor);
f.recipient.Assignment=nullptr;f.recipient.Downlink=nullptr;f.recipient.Waypoint=nullptr;f.recipient.Radio=&f;f.recipient.eligible=true;
assert(f.Plan());assert(f.s.m_Trip==1&&f.s.m_Consumed.Get("recipient")==1&&f.recipient.LogisticsReservation==100001);}
{Fixture f;f.s.Stock(0);f.recipient.LogisticsReservation=7;assert(!f.Plan());assert(f.recipient.LogisticsReservation==7&&f.s.m_Trip==0&&f.hq.downlinks==0);}
// Failed truck downlink is still a consumed planning attempt; cleanup preserves all cargo and other ownership.
{Fixture f;f.s.Stock(0);f.hq.routeAvailable=false;assert(f.Plan());assert(f.s.m_State==1&&f.s.m_Trip==1&&f.s.m_Consumed.Get("recipient")==1&&f.recipient.LogisticsReservation==0&&f.truck.SupplyToken==0&&f.inventory.items.Count()==48);
f.hq.routeAvailable=true;f.Refresh(180000,1);assert(!f.Plan(180000)); // same consumed message cannot replay after cooldown
f.Refresh(179999,2);assert(!f.Plan(179999));f.Refresh(180000,2);assert(f.Plan(180000));assert(f.s.m_Trip==2&&f.s.m_Deadline==300000);}
// A stale-but-still-admissible demand gets only the remaining evidence horizon, never a new full lifetime.
{Fixture f;f.s.Stock(0);assert(f.Plan(120000));assert(f.s.m_Deadline==180000);assert(!f.Plan(120000)&&f.hq.downlinks==1);}
// Independent guarded failures do not leak reservations or acquire replacement force identities.
for(int refusal=0;refusal<7;refusal++){Fixture f;f.s.Stock(0);
if(refusal==0){f.recipient.HomeGuard=true;}if(refusal==1){f.truck.Combat->Held=true;}if(refusal==2){f.vehicle.captured=true;}if(refusal==3){f.mode.ledger.record.Eliminated=true;}if(refusal==4){f.donor.PendingConvoyOrder=&f;}if(refusal==5){f.ammo.Complete=false;}if(refusal==6){f.demand.Position.x=201;}
assert(!f.Plan());f.Unchanged();}
}
''')
print('PASS: actual stock capture, unique finite identities, original recipients, HQ reservations, delivered report replay/cooldown and failed-downlink cleanup')
@@ -0,0 +1,49 @@
"""Actual fixture recipient/Stop methods with native boundaries doubled."""
import os,re,sys
from pathlib import Path
REPO=Path(os.environ.get('ERR_REPO','/home/jakub/devel/escapistsrereintroduced'))
sys.path.insert(0,str(REPO/'tests'))
from policy_harness import body,run
P=Path(os.environ.get('ERR_INFANTRY_PROBE_SOURCE',REPO/'addons/chat/Scripts/Game/ERR/ERR_NativeInfantryTransferProbe.c'))
s='\n'.join(body(P,x) for x in ('protected bool RecipientOwned()','void Stop()'))
s=s.replace('protected ','').replace('ERR_EnemyEquipmentRules.','Rules::').replace('CompartmentAccessComponent.','Compartment::')
for v in ('m_Unit','m_Mode','m_Actor','m_Move','m_HQ','m_Truck'):s=s.replace(v+'.',v+'->')
s=s.replace('StatusSampler.','StatusSampler->')
run(r'''
#include <cassert>
#include <vector>
struct Mode{bool active=true;static inline Mode* current=nullptr;};
struct Group{};
struct Actor{bool player=false,alive=true,seated=false;Group* group=nullptr;int moved=0,last=-1;void SetOrigin(int p){moved++;last=p;}};
struct Sampler{Group* group;int strength=1;bool released=false;std::vector<Actor*> original;bool OwnsRoster(Group* g){return g==group;}int InitialStrength(){return strength;}Actor* InitialMember(int i){return i<(int)original.size()?original[i]:nullptr;}bool CanAttribute(int,Actor*){return !released;}};
struct Unit{struct Group* Group;Sampler* StatusSampler;};
struct Rules{static inline bool server=true;static bool Eligible(Mode* mode,Group* group,Actor* actor){return server&&mode&&mode==Mode::current&&mode->active&&actor&&actor->alive&&!actor->player&&actor->group==group;}};
struct Compartment{static bool GetVehicleIn(Actor* a){return a->seated;}};
struct HQ{int token=200001,calls=0;int LogisticsToken(Group*){return token;}void ReleaseLogistics(Group*,int own){calls++;if(token==own)token=0;}};
struct Truck{int SupplyToken=200001;bool SupplyHeld=true;};
struct Move{int stops=0;bool unresolved=false;void Stop(){stops++;}bool Unresolved(){return unresolved;}};
struct Probe{static constexpr int TOKEN=200001;Mode* m_Mode;Unit* m_Unit;Actor* m_Actor;Move* m_Move=nullptr;HQ* m_HQ;Truck* m_Truck;bool m_Stopped=false,m_Positioned=true;int m_OriginalPosition=45;
METHODS
};
struct F{Mode mode;Group group;Actor actor;Sampler sampler;Unit unit;HQ hq;Truck truck;Probe probe;F():sampler{&group,1,false,{&actor}},unit{&group,&sampler},probe{&mode,&unit,&actor,nullptr,&hq,&truck}{Mode::current=&mode;actor.group=&group;}};
int main(){
{F f;assert(f.probe.RecipientOwned());f.actor.player=true;assert(!f.probe.RecipientOwned());f.probe.Stop();assert(f.actor.moved==0&&f.hq.token==0&&f.truck.SupplyToken==0);}
{F f;f.probe.Stop();assert(f.actor.moved==1&&f.actor.last==45&&!f.truck.SupplyHeld&&f.hq.calls==1);f.probe.Stop();assert(f.actor.moved==1&&f.hq.calls==1);}
{F f;Move move;move.unresolved=true;f.probe.m_Move=&move;f.probe.Stop();assert(move.stops==1&&f.actor.moved==0&&f.probe.m_Move==&move&&f.hq.token==0);}
{F f;Move move;f.probe.m_Move=&move;f.probe.Stop();assert(move.stops==1&&f.actor.moved==1);}
{F f;f.hq.token=55;f.truck.SupplyToken=55;f.probe.Stop();assert(f.hq.token==55&&f.truck.SupplyToken==55&&f.truck.SupplyHeld&&f.actor.moved==0);}
{F f;f.sampler.released=true;assert(!f.probe.RecipientOwned());f.probe.Stop();assert(f.actor.moved==0);}
{F f;f.actor.seated=true;f.probe.Stop();assert(f.actor.moved==0);}
{F f;Group foreign;f.actor.group=&foreign;f.probe.Stop();assert(f.actor.moved==0);}
{F f;Mode::current=nullptr;f.probe.Stop();assert(f.actor.moved==0);}
{F f;f.probe.m_Mode=nullptr;Mode::current=nullptr;f.probe.Stop();assert(f.actor.moved==0);}
{F f;f.mode.active=false;f.probe.Stop();assert(f.actor.moved==0);}
{F f;Rules::server=false;f.probe.Stop();assert(f.actor.moved==0);Rules::server=true;}
{F f;f.sampler.strength=33;assert(!f.probe.RecipientOwned());f.sampler.strength=0;assert(!f.probe.RecipientOwned());f.sampler.strength=1;f.sampler.original.clear();assert(!f.probe.RecipientOwned());}
}
'''.replace('METHODS',s))
place=body(P,'protected int Place(float now)')
assert place.index('if (!RecipientOwned()) return -1;')<place.index('m_Actor.SetOrigin(')
tick=body(P,'int Tick(ERR_HQ hq, float now)')
assert '&& RecipientOwned() && hq.LogisticsToken(m_Unit.Group) == TOKEN' in tick
print('PASS: actual fixture current/original recipient ownership, unresolved transfer cleanup, exact-token release and idempotent Stop')