78 lines
6.9 KiB
Python
78 lines
6.9 KiB
Python
"""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')
|