Fix inventory layout integration and qualify ordinary UI and radio guidance

This commit is contained in:
2026-09-19 18:17:51 +02:00
parent 5c7719c19c
commit 67de3752fb
125 changed files with 18310 additions and 42 deletions
+74 -10
View File
@@ -4,8 +4,8 @@ P=ROOT/'addons/core/Scripts/Game/ERR/ERR_OrdinaryInputObserver.c'
def cpp(s):
s=s.replace('protected ', '').replace('out string nonce','string& nonce')
for t in ('ERR_GameMode','PlayerController','IEntity','ERR_PlayerLife','ERR_OrdinaryInputObserver','PlayerManager','SCR_InventoryMenuUI','Widget'):
s=s.replace(t+' mode',t+'* mode').replace(t+' controller',t+'* controller').replace(t+' actor',t+'* actor').replace(t+' life',t+'* life').replace(t+' request',t+'* request').replace(t+' current',t+'* current').replace(t+' manager',t+'* manager').replace(t+' menu',t+'* menu').replace(t+' root',t+'* root')
for v in ('mode','controller','actor','request','life','current','manager','menu','root','m_Mode','m_World','m_Controller','m_Actor','m_Character','m_Menu','s_Request'):
s=s.replace(t+' mode',t+'* mode').replace(t+' controller',t+'* controller').replace(t+' actor',t+'* actor').replace(t+' life',t+'* life').replace(t+' request',t+'* request').replace(t+' current',t+'* current').replace(t+' manager',t+'* manager').replace(t+' menu',t+'* menu').replace(t+' root',t+'* root').replace(t+' expectedMenu',t+'* expectedMenu').replace(t+' expectedRoot',t+'* expectedRoot').replace(t+' overlay',t+'* overlay')
for v in ('mode','controller','actor','request','life','current','manager','menu','root','m_Mode','m_World','m_Controller','m_Actor','m_Character','m_Menu','s_Request','expectedMenu','expectedRoot','m_CaptureMenu','m_CaptureRoot','overlay','m_aHitZonePoints'):
s=s.replace(v+'.',v+'->')
for scope in ('System','Replication','ERR_Phase','ERR_GameMode','ERR_Actors','SCR_InventoryMenuUI','ERR_OrdinaryInputObserverRules','vector','Math'):
s=s.replace(scope+'.',scope+'::')
@@ -13,11 +13,14 @@ def cpp(s):
s=s.replace('CharacterControllerComponent.Cast(actor->FindComponent(CharacterControllerComponent))','actor->FindComponent(0)').replace('FindComponent(CharacterControllerComponent)','FindComponent(0)')
s=s.replace('.Insert(request->Cancel)', '.Insert()').replace('.Remove(Cancel)', '.Remove()')
s=s.replace('.CallLater(request->Sample, 100, false)', '.CallLater()').replace('->CallLater(request->Sample, 100, false)', '->CallLater()').replace('->CallLater(Sample, 100, false)', '->CallLater()').replace('->Remove(Sample)', '->Remove()')
s=s.replace('->CallLater(CaptureLater, 300, false)', '->QueueCapture()').replace('->Remove(CaptureLater)', '->RemoveCapture()').replace('->CallLater(CaptureLater, 100, false)', '->QueueCapture()')
s=s.replace('string.Format', 'Format').replace('Widget* overlay;', 'Widget* overlay=null;')
s=s.replace('null','nullptr').replace('bool open;', 'bool open = false;')
s=s.replace('nonce.Length()', 'int(nonce.size())').replace('allowed.Contains(nonce[i])', '(allowed.find(nonce[i]) != string::npos)')
return s
state=cpp(body(P,'string ERR_InputObserverInventoryState('))
rules='\n'.join(cpp(body(P,x)) for x in ('static bool Nonce(', 'static bool Within(', 'static bool Stationary('))
methods='\n'.join(cpp(body(P,x)) for x in ('protected static bool Enabled(', 'static void Queue(', 'protected bool Valid(', 'protected void Record(', 'protected void Capture(', 'protected void Observe(', 'protected void Sample(', 'protected void End(', 'void Cancel(', 'static void ClearWorld('))
methods='\n'.join(cpp(body(P,x)) for x in ('protected static bool Enabled(', 'static void Queue(', 'protected bool Valid(', 'protected void Record(', 'protected void CancelCapture(', 'protected void Capture(', 'protected void CaptureLater(', 'protected void Observe(', 'protected void Sample(', 'protected void End(', 'void Cancel(', 'static void ClearWorld('))
run(r'''
#include <cassert>
#include <cmath>
@@ -25,6 +28,7 @@ run(r'''
#include <vector>
#include <map>
using string=std::string;using UUID=string;
string Format(string,bool pending,int stage,int points,bool present,bool visible){return std::to_string(pending)+","+std::to_string(stage)+","+std::to_string(points)+","+std::to_string(present)+","+std::to_string(visible);}
struct vector {float x=0,y=0,z=0;float Length(){return std::sqrt(x*x+y*y+z*z);}static float Distance(vector a,vector b){return vector{a.x-b.x,a.y-b.y,a.z-b.z}.Length();}};
namespace Math {float AbsFloat(float x){return std::abs(x);}}
namespace System {bool console=false;int now=0;std::map<string,string> cli;std::vector<string> shots;bool IsConsoleApp(){return console;}int GetTickCount(){return now;}bool GetCLIParam(string key,string& value){if(!cli.count(key))return false;value=cli[key];return true;}void MakeScreenshot(string path){shots.push_back(path);}}
@@ -40,13 +44,17 @@ struct Roster {IEntity* actor=nullptr;ERR_PlayerLife* life=nullptr;ERR_PlayerLif
struct Invoker {int inserted=0,removed=0;void Insert(){inserted++;}void Remove(){removed++;}};
struct ERR_GameMode {static ERR_GameMode* current;BaseWorld* world=nullptr;bool active=true;int phase=ERR_Phase::PRISON;Roster roster;Invoker stop;static ERR_GameMode* Get(){return current;}BaseWorld* GetWorld(){return world;}bool Active(){return active;}int Phase(){return phase;}Roster* Players(){return &roster;}Invoker& OnStop(){return stop;}};ERR_GameMode* ERR_GameMode::current=nullptr;
struct PlayerManager {PlayerController* controller=nullptr;IEntity* actor=nullptr;PlayerController* GetPlayerController(int){return controller;}IEntity* GetPlayerControlledEntity(int){return actor;}};
struct Widget {bool visible=true;bool IsVisible(){return visible;}};
struct SCR_InventoryMenuUI {static SCR_InventoryMenuUI* current;CharacterControllerComponent* character=nullptr;Widget* root=nullptr;static SCR_InventoryMenuUI* GetInventoryMenu(){return current;}CharacterControllerComponent* GetCharacterController(){return character;}Widget* GetRootWidget(){return root;}};SCR_InventoryMenuUI* SCR_InventoryMenuUI::current=nullptr;
struct Widget {bool visible=true,hierarchy=true;Widget* loading=nullptr;Widget* FindAnyWidget(string name){assert(name=="LoadingOverlay");return loading;}bool IsVisible(){return visible;}bool IsVisibleInHierarchy(){return visible&&hierarchy;}};
struct HitPoints {int count=0;int Count(){return count;}};
struct SCR_InventoryMenuUI {bool m_bProcessInitQueue=true;int m_eInitStage=7;HitPoints points;HitPoints* m_aHitZonePoints=&points;static SCR_InventoryMenuUI* current;CharacterControllerComponent* character=nullptr;Widget* root=nullptr;static SCR_InventoryMenuUI* GetInventoryMenu(){return current;}CharacterControllerComponent* GetCharacterController(){return character;}Widget* GetRootWidget(){return root;}
'''+state+r'''
};SCR_InventoryMenuUI* SCR_InventoryMenuUI::current=nullptr;
struct MenuManager {SCR_InventoryMenuUI* top=nullptr;SCR_InventoryMenuUI* GetTopMenu(){return top;}};
struct InputManager {float forward=0;float GetActionValue(string name){assert(name=="CharacterForward");return forward;}};
struct Callqueue {int queued=0,removed=0;void CallLater(){queued++;}void Remove(){removed++;}};
struct Callqueue {int queued=0,removed=0,captures=0,capturesRemoved=0;void CallLater(){queued++;}void Remove(){removed++;}void QueueCapture(){captures++;}void RemoveCapture(){capturesRemoved++;}};
struct Game {BaseWorld* world=nullptr;PlayerController* controller=nullptr;PlayerManager manager;MenuManager menus;InputManager input;Callqueue queue;BaseWorld* GetWorld(){return world;}PlayerController* GetPlayerController(){return controller;}PlayerManager* GetPlayerManager(){return &manager;}MenuManager* GetMenuManager(){return &menus;}InputManager* GetInputManager(){return &input;}Callqueue* GetCallqueue(){return &queue;}} game;Game* GetGame(){return &game;}
std::vector<string> events;int samples=0;
std::vector<string> events,inventoryStates,inventoryReasons;int samples=0;
void PrintFormat(const char*,string nonce,int,string reason,string state){assert(nonce=="Run42");inventoryStates.push_back(state);inventoryReasons.push_back(reason);}
void PrintFormat(const char*,string nonce,int,int,float,string detail){assert(nonce=="Run42");events.push_back(detail);}
void PrintFormat(const char*,string nonce,int,int,float,vector,float,vector,vector,bool){assert(nonce=="Run42");samples++;}
struct ERR_OrdinaryInputObserverRules {
@@ -56,14 +64,16 @@ struct ERR_OrdinaryInputObserver {
static ERR_OrdinaryInputObserver* s_Request;static bool s_Attempted;
ERR_GameMode* m_Mode=nullptr;BaseWorld* m_World=nullptr;PlayerController* m_Controller=nullptr;IEntity* m_Actor=nullptr;CharacterControllerComponent* m_Character=nullptr;ERR_PlayerLife* m_Life=nullptr;
UUID m_Account;int m_PlayerId=0;string m_Nonce;bool m_Screenshots=false,m_Finished=false,m_Ready=false,m_Opened=false,m_Closed=false;SCR_InventoryMenuUI* m_Menu=nullptr;
string m_CaptureSuffix;SCR_InventoryMenuUI* m_CaptureMenu=nullptr;Widget* m_CaptureRoot=nullptr;int m_CaptureDue=0,m_CaptureExpires=0,m_CaptureLoadedAt=-1,m_InventoryStateLines=0;bool m_BaselineCaptureQueued=false,m_OpenCaptureQueued=false,m_ClosedCaptureQueued=false;
int m_Start=0,m_Previous=0,m_Samples=0,m_BaselineSince=-1;vector m_BaselineOrigin;float m_LastWorldTime=0;
'''+methods+r'''
};
ERR_OrdinaryInputObserver* ERR_OrdinaryInputObserver::s_Request=nullptr;bool ERR_OrdinaryInputObserver::s_Attempted=false;
struct Fixture {ERR_GameMode mode;BaseWorld world;PlayerController controller;IEntity actor;CharacterControllerComponent character;ERR_PlayerLife life;Widget widget;SCR_InventoryMenuUI menu;
Fixture(){game={};System::cli={{"errOrdinaryScenario","core"},{"errOrdinaryInputObserve","Run42"},{"errOrdinaryInputScreenshots","1"}};System::now=0;System::console=false;System::shots.clear();Replication::server=true;events.clear();samples=0;ERR_OrdinaryInputObserver::s_Request=nullptr;ERR_OrdinaryInputObserver::s_Attempted=false;SCR_InventoryMenuUI::current=nullptr;ERR_GameMode::current=&mode;mode.world=&world;mode.roster.actor=&actor;mode.roster.life=&life;actor.world=&world;actor.component=&character;controller.actor=&actor;game.world=&world;game.controller=&controller;game.manager.controller=&controller;game.manager.actor=&actor;menu.character=&character;menu.root=&widget;}
Fixture(){game={};System::cli={{"errOrdinaryScenario","core"},{"errOrdinaryInputObserve","Run42"},{"errOrdinaryInputScreenshots","1"}};System::now=0;System::console=false;System::shots.clear();Replication::server=true;events.clear();inventoryStates.clear();inventoryReasons.clear();samples=0;ERR_OrdinaryInputObserver::s_Request=nullptr;ERR_OrdinaryInputObserver::s_Attempted=false;SCR_InventoryMenuUI::current=nullptr;ERR_GameMode::current=&mode;mode.world=&world;mode.roster.actor=&actor;mode.roster.life=&life;actor.world=&world;actor.component=&character;controller.actor=&actor;game.world=&world;game.controller=&controller;game.manager.controller=&controller;game.manager.actor=&actor;menu.character=&character;menu.root=&widget;}
ERR_OrdinaryInputObserver* queue(){ERR_OrdinaryInputObserver::Queue(&mode,&controller,&actor);return ERR_OrdinaryInputObserver::s_Request;}
void sample(int ms){System::now=ms;world.time=ms;ERR_OrdinaryInputObserver::s_Request->Sample();}
void capture(int ms){System::now=ms;world.time=ms;ERR_OrdinaryInputObserver::s_Request->CaptureLater();}
void baseline(){for(int t=100;t<=2100;t+=100)sample(t);assert(ERR_OrdinaryInputObserver::s_Request->m_Ready);}
void open(){SCR_InventoryMenuUI::current=&menu;game.menus.top=&menu;}
void close(){SCR_InventoryMenuUI::current=nullptr;game.menus.top=nullptr;}
@@ -96,15 +106,66 @@ int main(){
if(fault==12)f.mode.roster.life=&otherLife;
if(fault==13)f.life.AccountId="changed";
assert(!p->Valid());f.sample(100);assert(p->m_Finished&&samples==0&&game.queue.removed==1&&f.mode.stop.removed==1);p->Cancel();assert(game.queue.removed==1);}
{Fixture f;auto p=f.queue();f.baseline();assert(count("READY")==1&&System::shots.size()==1);game.input.forward=1;f.character.input={0,0,1};f.actor.origin={0,0,1};f.sample(2200);assert(samples==22&&!p->m_Opened);game.input.forward=0;f.character.input={};f.character.disabled=true;f.open();f.sample(2300);assert(p->m_Opened&&count("INVENTORY_OPEN")==1&&System::shots.size()==2);f.sample(2400);assert(System::shots.size()==2);f.close();f.sample(2500);assert(p->m_Closed&&count("INVENTORY_CLOSED")==1&&System::shots.size()==3);f.sample(2600);assert(System::shots.size()==3);for(auto path:System::shots)assert(path.find("$logs:err-input-Run42-")==0);p->Cancel();int writes=samples;f.sample(2700);assert(samples==writes);ERR_OrdinaryInputObserver::ClearWorld();assert(ERR_OrdinaryInputObserver::s_Attempted&&!ERR_OrdinaryInputObserver::s_Request);assert(!f.queue());}
{Fixture f;auto p=f.queue();f.baseline();assert(count("READY")==1&&System::shots.empty()&&game.queue.captures==1);f.capture(2400);assert(System::shots.size()==1);game.input.forward=1;f.character.input={0,0,1};f.actor.origin={0,0,1};f.sample(2450);assert(samples==22&&!p->m_Opened);game.input.forward=0;f.character.input={};f.character.disabled=true;f.open();f.sample(2500);assert(p->m_Opened&&count("INVENTORY_OPEN")==1&&System::shots.size()==1);f.capture(2800);assert(System::shots.size()==1&&p->m_CaptureLoadedAt==2800&&p->m_CaptureDue==2900);f.capture(2900);assert(System::shots.size()==2);f.close();f.sample(3000);assert(p->m_Closed&&count("INVENTORY_CLOSED")==1&&System::shots.size()==2);f.capture(3300);assert(System::shots.size()==3);f.sample(3400);assert(System::shots.size()==3);for(auto path:System::shots)assert(path.find("$logs:err-input-Run42-")==0);p->Capture("baseline");p->Capture("open");p->Capture("closed");assert(game.queue.captures==4);p->Cancel();int writes=samples;f.sample(3500);assert(samples==writes);ERR_OrdinaryInputObserver::ClearWorld();assert(ERR_OrdinaryInputObserver::s_Attempted&&!ERR_OrdinaryInputObserver::s_Request);assert(!f.queue());}
{Fixture f;auto p=f.queue();System::cli["errOrdinaryInputScreenshots"]="0";p->m_Screenshots=false;f.open();for(int t=100;t<2300;t+=100)f.sample(t);assert(!p->m_Ready);f.close();for(int t=2300;t<=4300;t+=100)f.sample(t);assert(p->m_Ready&&System::shots.empty());}
for(int fault=0;fault<4;fault++){Fixture f;auto p=f.queue();f.baseline();f.open();
if(fault==0)f.menu.character=nullptr;
if(fault==1)f.menu.root=nullptr;
if(fault==2)f.widget.visible=false;
if(fault==3)game.menus.top=nullptr;
f.sample(2200);assert(p->m_Finished&&!p->m_Opened&&System::shots.size()==1);}
f.sample(2200);assert(p->m_Finished&&!p->m_Opened&&System::shots.empty());}
{Fixture f;auto p=f.queue();f.baseline();f.open();f.sample(2200);SCR_InventoryMenuUI replacement=f.menu;SCR_InventoryMenuUI::current=&replacement;game.menus.top=&replacement;f.sample(2300);assert(p->m_Finished&&!p->m_Closed);}
for(int fault=0;fault<14;fault++){Fixture f;auto p=f.queue();f.baseline();f.open();f.sample(2200);assert(p->m_CaptureMenu==&f.menu&&p->m_CaptureRoot==&f.widget);SCR_InventoryMenuUI otherMenu=f.menu;Widget otherRoot;IEntity otherActor;
if(fault==0)SCR_InventoryMenuUI::current=nullptr;
if(fault==1){SCR_InventoryMenuUI::current=&otherMenu;game.menus.top=&otherMenu;}
if(fault==2)f.menu.root=nullptr;
if(fault==3)f.menu.root=&otherRoot;
if(fault==4)f.widget.hierarchy=false;
if(fault==5)game.menus.top=nullptr;
if(fault==6)game.manager.actor=&otherActor;
if(fault==7)f.life.AccountId="changed";
if(fault==8)f.mode.phase=ERR_Phase::DEFEAT;
if(fault==9)p->m_CaptureMenu=nullptr;
if(fault==10)p->m_CaptureRoot=nullptr;
if(fault==11)p->m_Screenshots=false;
if(fault==12)f.menu.character=nullptr;
if(fault==13)p->m_LastWorldTime=3000;
f.capture(2500);assert(System::shots.empty()&&p->m_CaptureSuffix.empty()&&inventoryStates.empty());}
{Fixture f;auto p=f.queue();f.baseline();f.open();Widget loading;f.widget.loading=&loading;f.sample(2200);int expires=p->m_CaptureExpires;assert(expires==5200);f.capture(2500);assert(System::shots.empty()&&p->m_CaptureSuffix=="open"&&p->m_CaptureDue==2600&&p->m_CaptureExpires==expires);f.widget.loading=nullptr;f.capture(2600);assert(System::shots.empty()&&p->m_CaptureLoadedAt==2600&&p->m_CaptureExpires==expires);f.capture(2700);assert(System::shots.size()==1&&System::shots[0].find("-open")!=string::npos&&p->m_CaptureSuffix.empty());f.capture(2800);assert(System::shots.size()==1);}
{Fixture f;auto p=f.queue();f.baseline();f.open();Widget loading;f.widget.loading=&loading;f.sample(2200);for(int t=2500;t<=5100;t+=100){f.capture(t);assert(System::shots.empty());assert(p->m_CaptureExpires==5200||p->m_CaptureSuffix.empty());}assert(p->m_CaptureSuffix.empty()&&game.queue.captures<=28&&inventoryStates.size()<=30&&inventoryReasons.back()=="timeout");f.widget.loading=nullptr;f.capture(5200);assert(System::shots.empty());}
{Fixture f;auto p=f.queue();f.baseline();f.open();Widget loading;f.sample(2200);f.capture(2500);assert(System::shots.empty()&&p->m_CaptureLoadedAt==2500);f.widget.loading=&loading;f.capture(2600);assert(p->m_CaptureLoadedAt==-1&&p->m_CaptureExpires==5200);f.widget.loading=nullptr;f.capture(2700);assert(System::shots.empty()&&p->m_CaptureLoadedAt==2700);f.capture(2800);assert(System::shots.size()==1&&p->m_CaptureLoadedAt==-1);}
{Fixture f;auto p=f.queue();f.baseline();f.open();f.sample(2200);f.capture(5100);assert(System::shots.empty()&&p->m_CaptureSuffix.empty()&&p->m_CaptureLoadedAt==-1);}
{Fixture f;auto p=f.queue();f.baseline();f.open();f.sample(2200);f.capture(2500);assert(p->m_CaptureLoadedAt==2500);p->Cancel();assert(p->m_CaptureLoadedAt==-1&&p->m_CaptureSuffix.empty());}
for(int fault=0;fault<8;fault++){Fixture f;auto p=f.queue();f.baseline();f.open();Widget loading,otherRoot;SCR_InventoryMenuUI otherMenu=f.menu;IEntity otherActor;f.widget.loading=&loading;f.sample(2200);f.capture(2500);assert(p->m_CaptureSuffix=="open");
if(fault==0){SCR_InventoryMenuUI::current=&otherMenu;game.menus.top=&otherMenu;}
if(fault==1)f.menu.root=&otherRoot;
if(fault==2)game.menus.top=nullptr;
if(fault==3)f.widget.hierarchy=false;
if(fault==4)game.manager.actor=&otherActor;
if(fault==5)f.life.AccountId="changed";
if(fault==6)p->Cancel();
if(fault==7)ERR_OrdinaryInputObserver::ClearWorld();
f.widget.loading=nullptr;System::now=2600;f.world.time=2600;p->CaptureLater();assert(System::shots.empty()&&p->m_CaptureSuffix.empty()&&inventoryStates.size()==1);}
{Fixture f;auto p=f.queue();f.baseline();f.open();Widget loading;f.widget.loading=&loading;f.sample(2200);f.capture(2500);f.close();f.sample(2550);assert(p->m_CaptureSuffix=="closed"&&p->m_CaptureDue==2850&&System::shots.empty());f.capture(2850);assert(System::shots.size()==1&&System::shots[0].find("-closed")!=string::npos);}
{Fixture f;Widget loading;f.widget.loading=&loading;f.menu.points.count=3;assert(f.menu.ERR_InputObserverInventoryState()=="1,7,3,1,1");loading.hierarchy=false;assert(f.menu.ERR_InputObserverInventoryState()=="1,7,3,1,0");f.menu.m_aHitZonePoints=nullptr;f.menu.m_bProcessInitQueue=false;f.widget.loading=nullptr;assert(f.menu.ERR_InputObserverInventoryState()=="0,7,-1,0,0");}
{Fixture f;auto p=f.queue();f.baseline();f.open();Widget loading;f.widget.loading=&loading;f.sample(2200);p->m_InventoryStateLines=29;f.capture(2500);assert(p->m_InventoryStateLines==30&&inventoryStates.size()==1);f.capture(2600);assert(p->m_InventoryStateLines==30&&inventoryStates.size()==1&&p->m_CaptureSuffix=="open");}
{Fixture f;auto p=f.queue();f.baseline();f.open();f.sample(2200);f.capture(5200);assert(System::shots.empty()&&p->m_CaptureSuffix.empty()&&inventoryReasons.size()==1&&inventoryReasons[0]=="timeout");}
for(int fault=0;fault<4;fault++){Fixture f;auto p=f.queue();f.baseline();
if(fault==0)game.input.forward=1;
if(fault==1)f.character.input={0,0,1};
if(fault==2)f.character.velocity={0,0,1};
if(fault==3)f.actor.origin={0,0,.2};
f.capture(2400);assert(System::shots.empty()&&p->m_CaptureSuffix.empty());}
{Fixture f;auto p=f.queue();f.baseline();game.input.forward=1;f.sample(2200);assert(p->m_CaptureSuffix.empty());game.input.forward=0;f.capture(2400);assert(System::shots.empty());}
{Fixture f;f.queue();f.baseline();f.capture(2399);assert(System::shots.empty());}
{Fixture f;f.queue();f.baseline();f.capture(45000);assert(System::shots.empty());}
{Fixture f;f.queue();f.baseline();f.open();f.sample(2200);f.close();f.sample(2300);assert(game.queue.captures==3);f.capture(2600);assert(System::shots.size()==1&&System::shots[0].find("-closed")!=string::npos);}
{Fixture f;f.queue();f.baseline();f.open();f.capture(2400);assert(System::shots.empty());}
{Fixture f;f.queue();f.baseline();game.menus.top=&f.menu;f.capture(2400);assert(System::shots.empty());}
{Fixture f;auto p=f.queue();f.baseline();assert(!p->m_CaptureSuffix.empty());p->Cancel();assert(p->m_CaptureSuffix.empty()&&game.queue.capturesRemoved==1);f.capture(2500);assert(System::shots.empty());}
{Fixture f;auto p=f.queue();f.baseline();ERR_OrdinaryInputObserver::ClearWorld();assert(p->m_Finished&&p->m_CaptureSuffix.empty()&&game.queue.capturesRemoved==1);System::now=2500;p->CaptureLater();assert(System::shots.empty());}
{Fixture f;auto p=f.queue();f.sample(100);f.sample(200);f.sample(1000);assert(p->m_BaselineSince==-1);f.sample(1100);assert(!p->m_Ready);f.sample(45000);assert(p->m_Finished&&count("FINISHED observation-bound")==1);}
{Fixture f;auto p=f.queue();for(int t=1;t<=450;t++)f.sample(t);assert(p->m_Finished&&samples==450&&count("FINISHED sample-bound")==1);}
{Fixture f;auto p=f.queue();f.sample(100);System::now=200;f.world.time=99;p->Sample();assert(p->m_Finished&&count("CANCELLED world-clock-reversed")==1);}
@@ -117,6 +178,9 @@ for forbidden in ('SetActionValue(', 'ActivateContext(', 'OpenMenu(', 'CloseMenu
assert s.startswith('#ifndef WORKBENCH')
assert 's_Attempted = false' not in body(P,'static void ClearWorld(')
assert s.count('System.MakeScreenshot(')==1
assert 'ERR INVENTORY OBSERVE:' in s
assert 'm_InventoryStateLines < 30' in s
assert 'override' not in body(P,'string ERR_InputObserverInventoryState(')
for line in s.splitlines():
if 'PrintFormat(' in line:assert all(x not in line for x in ('m_Account','m_PlayerId','m_Life','m_Controller','m_Actor'))
print('PASS: actual observer nonce, admission pins, baseline/menu sequencing, snapshots, bounded clocks/samples, cleanup and one-attempt latch; native untested')