Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
194 lines
14 KiB
Python
194 lines
14 KiB
Python
"""Actual WB fixture lifecycle; native danger/radio delivery is deliberately doubled."""
|
|
import re
|
|
from policy_harness import ROOT,body,run
|
|
P=ROOT/'addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeAlarmHearingProbe.c'
|
|
s=P.read_text()
|
|
assert s.startswith('#ifdef WORKBENCH') and s.rstrip().endswith('#endif')
|
|
for forbidden in ('RequestBroadcastDangerEvent(', '.HearAlarm(', '.Hear(', '.Ingest(', '.Advance(', '.ERR_BindAwareness(', '.ActivateAI(', '.SetLOD(', '.SetPermanentLOD(', 'ReserveGroup(', '.ClearDangerEvents(', '.Remember('):
|
|
assert forbidden not in s,forbidden
|
|
assert s.count('ERR_VehicleAlarmPerception.Emit(')==1
|
|
queue=body(P,'protected void ObserveQueue(')
|
|
assert 'Math.Min(agent.GetDangerEventsCount(), 32)' in queue
|
|
assert 'agent.GetControlledEntity() != m_Witness' in queue
|
|
assert 'if (!m_Emitted' in queue
|
|
assert 'super.Update(utility, timeSlice)' in s
|
|
assert 'Diagnostic("before-emission")' in s and 'Diagnostic("cleanup")' in s
|
|
assert 'm_Deadline = now + 120000;' in s and s.count('m_Deadline =')==1
|
|
assert 'GetControlAIAgent().IsAIActivated()' in s
|
|
assert 'm_Unit.Detection == m_Unit.Utility.m_Perception' in s
|
|
assert 'm_HQ.ReserveLogistics(unit.Group, TOKEN)' in s
|
|
assert 'm_Record = m_Mode.Forces().FindGroup(unit.Group)' in s
|
|
assert 'volume.ExcludeArray = excluded' in s
|
|
|
|
def cpp(v):
|
|
v=v.replace('protected ','')
|
|
for name in ('ERR_GameMode','ERR_ForceRules','ERR_HQLocalAwareness','ERR_VehicleAlarmPerception','ERR_Actors','ERR_HQRadio','ChimeraCharacter'):
|
|
v=v.replace(name+'.',name+'::')
|
|
for name in ('hq','m_Mode','m_HQ','m_Unit','m_Witness','m_Observer','m_Vehicle','report','m_Route','m_Queued','delivered','local','pending','character','observerCharacter','volume'):
|
|
v=re.sub(r'\b'+name+r'\.',name+'->',v)
|
|
for name in ('GetGame()','GetWorld()','LocalAwareness','Players()'):
|
|
v=v.replace(name+'.',name+'->')
|
|
v=v.replace('m_Unit->Group','m_Unit->group').replace('m_Route->Nodes.Count()', 'm_Route->nodes')
|
|
v=v.replace('ChimeraCharacter.Cast(m_Observer).', 'ChimeraCharacter.Cast(m_Observer)->').replace('ChimeraCharacter.Cast(m_Witness).', 'ChimeraCharacter.Cast(m_Witness)->')
|
|
v=v.replace('ChimeraCharacter::Cast(m_Observer).', 'ChimeraCharacter::Cast(m_Observer)->').replace('ChimeraCharacter::Cast(m_Witness).', 'ChimeraCharacter::Cast(m_Witness)->')
|
|
v=v.replace('TraceBox volume', 'TraceBox* volume').replace('TraceFlags.ENTS', '1').replace('array<IEntity>', 'std::vector<Actor*>').replace('ChimeraCharacter observerCharacter', 'Actor* observerCharacter').replace('CompartmentAccessComponent.', 'CompartmentAccessComponent::')
|
|
for literal,value in [('20 0 0','20,0,0'),('0 0 300','0,0,300'),('0 1 0','0,1,0'),('0 0.01 0','0,0.01f,0'),('-0.3 -0.9 -0.3','-0.3f,-0.9f,-0.3f'),('0.3 0.9 0.3','0.3f,0.9f,0.3f')]:
|
|
v=v.replace('"'+literal+'"', 'vector{'+value+'}')
|
|
v=v.replace('ERR_HQ hq','HQ* hq').replace('ERR_HQReport report','Report* report').replace('ERR_HQReport local','Report* local').replace('ERR_HQReport pending','Report* pending').replace('ERR_HQBelief delivered','BeliefValue* delivered').replace('ChimeraCharacter character','Actor* character')
|
|
v=v.replace('vector.Distance(', 'vector::Distance(').replace('LogLevel.ERROR','0')
|
|
return v.replace('null','nullptr')
|
|
methods='\n'.join(cpp(body(P,sig)) for sig in ('protected bool Check(', 'protected bool Same(', 'protected bool ObserverOriginal(', 'protected bool ClearPoint(', 'protected bool Arrange(', 'int Tick(', 'void Stop('))
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <string>
|
|
#include <vector>
|
|
using string=std::string;
|
|
struct vector{float x=0,y=0,z=0;vector operator+(vector other){return {x+other.x,y+other.y,z+other.z};}bool operator==(vector other){return x==other.x&&y==other.y&&z==other.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 Actor{vector position;bool alive=true,aboard=false;int teleports=0;vector restored[4];vector GetOrigin(){return position;}
|
|
void GetWorldTransform(vector* out){out[0]={0,0,1};out[1]={0,1,0};out[2]={-1,0,0};out[3]=position;}
|
|
void Teleport(vector* transform){teleports++;for(int i=0;i<4;i++)restored[i]=transform[i];position=transform[3];}};
|
|
struct ChimeraCharacter{static Actor* Cast(Actor* actor){return actor;}};
|
|
struct CompartmentAccessComponent{static Actor* GetVehicleIn(Actor* actor){return actor&&actor->aboard?actor:nullptr;}};
|
|
struct ERR_Actors{static vector Ground(vector position){return position;}static bool Alive(Actor* a){return a&&a->alive;}static bool CanFight(Actor* a){return Alive(a);}};
|
|
struct ERR_HQRadio{inline static int capability=2;static int Capability(Actor*){return capability;}};
|
|
struct ERR_ForceRules{static bool Finite(float value){return std::isfinite(value);}};
|
|
struct ERR_HQLocalAwareness{static vector SoundArea(vector p){return {std::floor(p.x/150)*150+75,p.y,std::floor(p.z/150)*150+75};}};
|
|
struct Report{void* Source=nullptr;void* Target=nullptr;string TravelMode="heard vehicle alarm";float ObservedAt=-1;vector Position;
|
|
bool HasVelocity=false,Distress=false;int SeenEscapists=-1,MessageId=0;Report* Snapshot(){return new Report(*this);}};
|
|
struct Cache{Report* local=nullptr;Report* ERR_AlarmFixtureLocal(){return local?local->Snapshot():nullptr;}};
|
|
struct Route{bool Failed=false,Sending=false;int Cursor=0,nodes=1,MessageId=0;};
|
|
struct Unit{int Id=1;void* group=this;Cache cache;Cache* LocalAwareness=&cache;Report* PendingReport=nullptr;void* PendingStatus=nullptr;Route* Uplink=nullptr;};
|
|
struct HQ{bool registered=true;int token=220002,releases=0;
|
|
bool ERR_AlarmFixtureRegistered(Unit*){return registered;}int LogisticsToken(void*){return token;}
|
|
void ReleaseLogistics(void*,int expected){if(token==expected){token=0;releases++;}}};
|
|
struct Roster{Actor* current=nullptr;bool CurrentActor(Actor* actor){return actor&&actor==current;}};
|
|
struct ERR_GameMode{Roster roster;Roster* Players(){return &roster;}inline static ERR_GameMode* current=nullptr;bool active=true;static ERR_GameMode* Get(){return current;}bool Active(){return active;}};
|
|
struct TraceBox{vector Start,End,Mins,Maxs;int Flags=0;std::vector<Actor*> ExcludeArray;};
|
|
struct World{float time=100;int traces=0,blocked=0;float GetWorldTime(){return time;}float TraceMove(TraceBox* volume,void*){traces++;assert(volume->ExcludeArray.size()==2);return traces==blocked?0.f:1.f;}};
|
|
struct Game{World world;bool worldAvailable=true;World* GetWorld(){return worldAvailable?&world:nullptr;}};
|
|
Game* game=nullptr;Game* GetGame(){return game;}
|
|
struct ERR_VehicleAlarmPerception{inline static int emitted=0;static void Emit(Actor*){emitted++;}};
|
|
int failures=0;void Print(string,int=1){}
|
|
template<class... T>void PrintFormat(string,T...){}
|
|
struct BeliefValue{struct Report* Report=nullptr;int MessageId=0;float ObservedAt=0;void* Target=nullptr;};
|
|
struct Probe{
|
|
static const int TOKEN=220002;ERR_GameMode* m_Mode=nullptr;HQ* m_HQ=nullptr;Unit* m_Unit=nullptr;
|
|
Actor* m_Observer=nullptr;bool m_ObserverMoved=false;vector m_ObserverOriginal[4];vector m_Destination;Actor* m_Witness=nullptr;Actor* m_Vehicle=nullptr;vector m_Original[4];vector m_AlarmPosition;
|
|
Route* m_Route=nullptr;Report* m_Queued=nullptr;float m_ObservedAt=-1,m_Deadline=120000,m_Wait=0;
|
|
int m_State=0;bool m_Moved=false,m_Emitted=false,m_Stopped=false;
|
|
inline static Probe* s_Current=nullptr;int diagnostics=0;bool receiverReady=true;void Diagnostic(string){diagnostics++;}bool ReceiverReady(){return receiverReady;}
|
|
bool enabled=true,original=true,bound=true,selected=true,arranged=true;BeliefValue* belief=nullptr;int arrangements=0;
|
|
bool Enabled(){return enabled;}bool Original(Unit*,Actor*){return original;}bool Bound(){return bound;}
|
|
bool Select(){return selected;}BeliefValue* Belief(){return belief;}
|
|
'''+methods+r'''
|
|
};
|
|
struct Fixture{
|
|
Game context;ERR_GameMode mode;HQ hq;Unit unit;Actor witness,vehicle,observer;Probe probe;Route route;Report report;BeliefValue belief;
|
|
Fixture(){
|
|
game=&context;ERR_GameMode::current=&mode;ERR_HQRadio::capability=2;ERR_VehicleAlarmPerception::emitted=0;
|
|
observer.position={1000,10,1000};mode.roster.current=&observer;probe.m_Observer=&observer;probe.m_Mode=&mode;probe.m_Unit=&unit;probe.m_Witness=&witness;probe.m_Vehicle=&vehicle;
|
|
probe.m_Original[0]={1,0,0};probe.m_Original[1]={0,1,0};probe.m_Original[2]={0,0,1};probe.m_Original[3]={9,8,7};
|
|
witness.position={20,0,0};vehicle.position={0,0,0};
|
|
}
|
|
void emit(){assert(probe.Tick(&hq,100)==0&&probe.m_State==1&&witness.teleports==1&&observer.teleports==1);
|
|
context.world.time=1600;assert(probe.Tick(&hq,1599)==0&&ERR_VehicleAlarmPerception::emitted==0);
|
|
assert(probe.Tick(&hq,1600)==0&&probe.m_State==2&&ERR_VehicleAlarmPerception::emitted==1);}
|
|
void pending(){report.Source=unit.group;report.Position=ERR_HQLocalAwareness::SoundArea({0,0,0});report.ObservedAt=1600;report.MessageId=7;
|
|
unit.PendingReport=&report;unit.Uplink=&route;route.MessageId=7;}
|
|
};
|
|
int main(){
|
|
// Both clear volumes are validated before either original body is moved.
|
|
for(int blocked:{1,2}){
|
|
Fixture f;f.context.world.blocked=blocked;
|
|
assert(f.probe.Tick(&f.hq,100)==-1&&f.witness.teleports==0&&f.observer.teleports==0);
|
|
}
|
|
{
|
|
Fixture f;assert(f.probe.Tick(&f.hq,100)==0);
|
|
assert(f.observer.position==vector({0,0,300})&&f.witness.position==vector({20,0,0}));
|
|
f.probe.receiverReady=false;assert(f.probe.Tick(&f.hq,1600)==0&&ERR_VehicleAlarmPerception::emitted==0);
|
|
f.context.world.time=9000;f.probe.receiverReady=true;assert(f.probe.Tick(&f.hq,9000)==0&&ERR_VehicleAlarmPerception::emitted==1&&f.probe.m_ObservedAt==9000);
|
|
f.probe.Stop();assert(f.observer.position==vector({1000,10,1000})&&f.observer.teleports==2);
|
|
assert(f.observer.restored[0]==vector({0,0,1})&&f.observer.restored[2]==vector({-1,0,0}));
|
|
}
|
|
// Canonical observer loss never moves its replacement and never prevents
|
|
// restoration of the still-owned witness; inverse loss restores observer.
|
|
for(int fault=0;fault<3;fault++){
|
|
Fixture f;f.emit();Actor replacement;
|
|
if(fault==0)f.mode.roster.current=&replacement;
|
|
if(fault==1)f.observer.alive=false;
|
|
if(fault==2)f.observer.aboard=true;
|
|
assert(f.probe.Tick(&f.hq,1800)==-1&&f.observer.teleports==1&&replacement.teleports==0&&f.witness.teleports==2);
|
|
}
|
|
{
|
|
Fixture f;f.emit();f.probe.original=false;f.probe.Stop();
|
|
assert(f.witness.teleports==1&&f.observer.teleports==2&&f.observer.position==vector({1000,10,1000}));
|
|
}
|
|
{
|
|
Fixture f;assert(f.probe.Tick(&f.hq,100)==0);f.probe.receiverReady=false;
|
|
assert(f.probe.Tick(&f.hq,1600)==0&&!f.probe.m_Stopped&&ERR_VehicleAlarmPerception::emitted==0);
|
|
assert(f.probe.Tick(&f.hq,119999)==0&&f.probe.m_Deadline==120000);
|
|
assert(f.probe.Tick(&f.hq,120000)==-1&&f.witness.teleports==2&&f.observer.teleports==2);
|
|
}
|
|
{
|
|
Fixture f;f.emit();f.vehicle.position={900,0,0};
|
|
assert(f.probe.Tick(&f.hq,1700)==0&&f.probe.m_State==2&&ERR_VehicleAlarmPerception::emitted==1);
|
|
// Explicit native/radio stand-ins establish only fixture verification, never
|
|
// actual danger routing. The fixture itself cannot manufacture these states.
|
|
f.pending();assert(f.probe.Tick(&f.hq,1800)==0&&f.probe.m_State==3);
|
|
f.route.Cursor=1;f.belief.Report=&f.report;f.belief.MessageId=7;f.belief.ObservedAt=1600;f.probe.belief=&f.belief;
|
|
assert(f.probe.Tick(&f.hq,8000)==1&&f.probe.m_Stopped&&f.hq.releases==1&&f.witness.teleports==2);
|
|
assert(f.witness.position==f.probe.m_Original[3]&&f.witness.restored[0]==f.probe.m_Original[0]);
|
|
f.probe.Stop();assert(f.witness.teleports==2&&f.hq.releases==1&&ERR_VehicleAlarmPerception::emitted==1);
|
|
}
|
|
{
|
|
Fixture f;f.probe.selected=false;assert(f.probe.Tick(&f.hq,119999)==0);
|
|
assert(f.probe.Tick(&f.hq,120000)==-1&&f.probe.m_Stopped&&ERR_VehicleAlarmPerception::emitted==0); // fixed deadline never renewed
|
|
}
|
|
{
|
|
Fixture f;assert(f.probe.Tick(&f.hq,100)==0);f.unit.PendingStatus=&f.unit;
|
|
assert(f.probe.Tick(&f.hq,1600)==0&&f.probe.m_State==1&&ERR_VehicleAlarmPerception::emitted==0);
|
|
assert(f.probe.Tick(&f.hq,120000)==-1&&f.witness.teleports==2);
|
|
}
|
|
for(int fault=0;fault<7;fault++){
|
|
Fixture f;f.emit();f.pending();
|
|
if(fault==0)f.report.ObservedAt=1800;
|
|
if(fault==1)f.report.Target=&f.vehicle;
|
|
if(fault==2)f.report.Position={0,0,0};
|
|
if(fault==3)f.report.Source=&f.vehicle;
|
|
if(fault==4)f.report.HasVelocity=true;
|
|
if(fault==5)f.report.SeenEscapists=1;
|
|
if(fault==6)f.report.Distress=true;
|
|
assert(f.probe.Tick(&f.hq,1800)==-1&&f.probe.m_Stopped&&f.witness.teleports==2);
|
|
}
|
|
for(int fault=0;fault<4;fault++){
|
|
Fixture f;f.emit();f.pending();assert(f.probe.Tick(&f.hq,1800)==0);
|
|
f.route.Cursor=1;f.belief.Report=&f.report;f.belief.MessageId=7;f.belief.ObservedAt=1600;f.probe.belief=&f.belief;
|
|
if(fault==0)f.route.Cursor=0;
|
|
if(fault==1)f.route.Sending=true;
|
|
if(fault==2)f.belief.MessageId=8;
|
|
if(fault==3)f.belief.ObservedAt=1800;
|
|
assert(f.probe.Tick(&f.hq,8000)==-1&&f.probe.m_Stopped);
|
|
}
|
|
{
|
|
Fixture f;f.emit();f.pending();assert(f.probe.Tick(&f.hq,1800)==0);f.route.Failed=true;
|
|
assert(f.probe.Tick(&f.hq,1801)==-1&&f.witness.teleports==2);
|
|
}
|
|
// Loss of original actor/registry/reservation/mode must never move a foreign
|
|
// replacement body or steal its reservation during cleanup.
|
|
for(int fault=0;fault<4;fault++){
|
|
Fixture f;f.emit();ERR_GameMode replacement;
|
|
if(fault==0)f.probe.original=false;
|
|
if(fault==1)f.hq.registered=false;
|
|
if(fault==2)f.hq.token=99;
|
|
if(fault==3)ERR_GameMode::current=&replacement;
|
|
f.probe.Stop();assert(f.witness.teleports==1);if(fault==2)assert(f.hq.token==99);
|
|
}
|
|
{
|
|
Fixture f;f.emit();f.probe.bound=false;assert(f.probe.Tick(&f.hq,1800)==-1&&f.probe.m_Stopped);
|
|
}
|
|
}
|
|
''')
|
|
print('PASS: actual arranged native alarm fixture deadline, single emit, natural-route observation, original timestamp/area/detail assertions, natural observer proximity/activation wait, failure cleanup and independent canonical-body restoration; native delivery itself remains unqualified.')
|