87 lines
9.8 KiB
Python
87 lines
9.8 KiB
Python
"""Execute candidate hierarchy adapter; native slot regeneration/rendering unqualified."""
|
|
from policy_harness import ROOT, body, run
|
|
P=ROOT/'addons/core/Scripts/Game/ERR/ERR_InventoryLayout.c'
|
|
def cpp(s):
|
|
s=s.replace('protected ', '').replace('override ', '').replace('ref ', '')
|
|
for t in ('Widget','ERR_GameMode','GridLayoutWidget','ERR_InventoryGridPlacement'):
|
|
for n in ('widget','child','w','mode','root','grid','named','placement','previousHost','host'):
|
|
s=s.replace(t+' '+n+'(',t+'* '+n+'(').replace(t+' '+n+')',t+'* '+n+')').replace(t+' '+n+' =',t+'* '+n+' =')
|
|
s=s.replace('GridLayoutWidget.Cast(', 'dynamic_cast<GridLayoutWidget*>(').replace('VerticalLayoutWidget.Cast(', 'dynamic_cast<VerticalLayoutWidget*>(')
|
|
for n in ('widget','child','w','mode','root','grid','named','placement','previousHost','host','m_ERRStorageLayoutRoot','m_ERRStorageLayoutGrid','m_ERRStorageLayoutHost','m_MenuHandler'):
|
|
s=s.replace(n+'.',n+'->')
|
|
for n in ('ERR_GameMode','SCR_InventoryMenuUI','WidgetType','WidgetFlags','GridSlot','AlignableSlot','LayoutHorizontalAlign','LayoutVerticalAlign'):
|
|
s=s.replace(n+'.',n+'::')
|
|
s=s.replace('GetGame().','GetGame()->').replace('GetWorkspace().','GetWorkspace()->')
|
|
s=s.replace('null','nullptr').replace('bool created;', 'bool created = false;')
|
|
return s.replace('super.', 'Native::')
|
|
placement='\n'.join(cpp(body(P,k)) for k in ('bool Capture(', 'bool Apply('))
|
|
prepare=cpp(body(P,'bool ERR_PrepareStorageLayout('))
|
|
attach=cpp(body(P,'override void HandlerAttached('))
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
using string=std::string;
|
|
namespace LayoutHorizontalAlign{enum{Left,Center,Right,Stretch};}
|
|
namespace LayoutVerticalAlign{enum{Top,Center,Bottom,Stretch};}
|
|
namespace WidgetType{const int VerticalLayoutWidgetTypeID=7;}
|
|
namespace WidgetFlags{const int VISIBLE=1;}
|
|
struct Widget {virtual ~Widget(){}Widget* parent=nullptr;std::vector<Widget*> children;string name;bool removed=false,refuseAdd=false;int row=0,col=0,rows=1,cols=1,h=0,v=0;float l=0,t=0,r=0,b=0;int handlers=0;
|
|
Widget* GetParent(){return parent;}Widget* GetChildren(){return children.empty()?nullptr:children[0];}string GetName(){return name;}void SetName(string n){name=n;}
|
|
void AddChild(Widget* child){if(refuseAdd)return;if(child->parent){auto& a=child->parent->children;a.erase(std::remove(a.begin(),a.end(),child),a.end());}child->parent=this;children.push_back(child);child->row=child->col=0;child->rows=child->cols=1;child->l=child->t=child->r=child->b=0;child->h=child->v=0;}
|
|
Widget* FindAnyWidget(string n){if(name==n)return this;for(auto c:children)if(auto result=c->FindAnyWidget(n))return result;return nullptr;}
|
|
void RemoveFromHierarchy(){removed=true;if(parent){auto& a=parent->children;a.erase(std::remove(a.begin(),a.end(),this),a.end());}parent=nullptr;}
|
|
};
|
|
struct GridLayoutWidget:Widget{};struct VerticalLayoutWidget:Widget{};
|
|
namespace GridSlot {void Check(Widget* w){assert(w&&dynamic_cast<GridLayoutWidget*>(w->parent));}int GetRow(Widget*w){Check(w);return w->row;}int GetColumn(Widget*w){Check(w);return w->col;}int GetRowSpan(Widget*w){Check(w);return w->rows;}int GetColumnSpan(Widget*w){Check(w);return w->cols;}void SetRow(Widget*w,int n){Check(w);w->row=n;}void SetColumn(Widget*w,int n){Check(w);w->col=n;}void SetRowSpan(Widget*w,int n){Check(w);w->rows=n;}void SetColumnSpan(Widget*w,int n){Check(w);w->cols=n;}}
|
|
namespace AlignableSlot {int GetHorizontalAlign(Widget*w){return w->h;}int GetVerticalAlign(Widget*w){return w->v;}void GetPadding(Widget*w,float&l,float&t,float&r,float&b){l=w->l;t=w->t;r=w->r;b=w->b;}void SetHorizontalAlign(Widget*w,int n){w->h=n;}void SetVerticalAlign(Widget*w,int n){w->v=n;}void SetPadding(Widget*w,float l,float t,float r,float b){w->l=l;w->t=t;w->r=r;w->b=b;}}
|
|
struct Workspace {int creates=0;bool fail=false,wrongType=false,refuseAdd=false;Widget* last=nullptr;Widget* CreateWidget(int type,int flags,void*,int,Widget* parent){assert(type==7&&flags==1);creates++;if(fail)return nullptr;Widget* host=wrongType?new Widget:new VerticalLayoutWidget;parent->AddChild(host);host->refuseAdd=refuseAdd;last=host;return host;}};
|
|
struct ERR_GameMode {static ERR_GameMode* current;bool active=true;int world=1;static ERR_GameMode* Get(){return current;}bool Active(){return active;}int GetWorld(){return world;}};ERR_GameMode* ERR_GameMode::current=nullptr;
|
|
struct Game {Workspace workspace;bool menu=true;int world=1;Workspace* GetWorkspace(){return &workspace;}bool GetMenuManager(){return menu;}int GetWorld(){return world;}} game;Game* GetGame(){return &game;}
|
|
struct ERR_InventoryGridPlacement {int m_Row=0,m_Column=0,m_RowSpan=0,m_ColumnSpan=0,m_Horizontal=0,m_Vertical=0;float m_Left=0,m_Top=0,m_Right=0,m_Bottom=0;
|
|
'''+placement+r'''
|
|
};
|
|
struct SCR_InventoryMenuUI {static SCR_InventoryMenuUI* current;Widget* root=nullptr;Widget* m_ERRStorageLayoutRoot=nullptr;GridLayoutWidget* m_ERRStorageLayoutGrid=nullptr;VerticalLayoutWidget* m_ERRStorageLayoutHost=nullptr;bool m_ERRStorageLayoutBound=false;static SCR_InventoryMenuUI* GetInventoryMenu(){return current;}Widget* GetRootWidget(){return root;}
|
|
'''+prepare+r'''
|
|
};SCR_InventoryMenuUI* SCR_InventoryMenuUI::current=nullptr;
|
|
struct Native {int calls=0,baseInit=0,refresh=0,invalidLayoutCalls=0;void HandlerAttached(Widget* w){calls++;baseInit++;if(!w||!dynamic_cast<VerticalLayoutWidget*>(w->parent))invalidLayoutCalls+=2;refresh++;}};
|
|
struct Handler:Native {SCR_InventoryMenuUI* m_MenuHandler=nullptr;
|
|
'''+attach+r'''
|
|
};
|
|
struct Fixture {ERR_GameMode mode;Widget root,child;GridLayoutWidget grid;SCR_InventoryMenuUI menu;Handler handler;
|
|
Fixture(){game={};ERR_GameMode::current=&mode;SCR_InventoryMenuUI::current=&menu;menu.root=&root;grid.name="StoragesListSlot";root.AddChild(&grid);child.name="Frame0";grid.AddChild(&child);child.row=2;child.col=3;child.rows=4;child.cols=5;child.l=1;child.t=2;child.r=3;child.b=4;child.h=2;child.v=1;handler.m_MenuHandler=&menu;}
|
|
void attach(){handler.HandlerAttached(&child);assert(handler.calls==1&&handler.baseInit==1&&handler.refresh==1);}
|
|
};
|
|
int main(){
|
|
{Fixture f;f.attach();auto host=f.menu.m_ERRStorageLayoutHost;assert(host&&host->parent==&f.grid&&f.child.parent==host&&game.workspace.creates==1&&f.handler.invalidLayoutCalls==0);assert(host->row==2&&host->col==3&&host->rows==4&&host->cols==5&&host->l==1&&host->t==2&&host->r==3&&host->b==4&&host->h==2&&host->v==1);assert(f.child.l==0&&f.child.t==0&&f.child.r==0&&f.child.b==0&&f.child.h==3&&f.child.v==3);
|
|
// Native refresh removes the old root; the same menu reuses its empty host.
|
|
f.child.RemoveFromHierarchy();Widget next;next.name="Frame0";f.grid.AddChild(&next);Handler second;second.m_MenuHandler=&f.menu;second.HandlerAttached(&next);assert(second.calls==1&&second.baseInit==1&&second.refresh==1&&second.invalidLayoutCalls==0&&game.workspace.creates==1&&next.parent==host&&f.grid.children.size()==1);
|
|
}
|
|
for(int fault=0;fault<8;fault++){Fixture f;SCR_InventoryMenuUI other;Widget foreignRoot;GridLayoutWidget foreignGrid;
|
|
if(fault==0)ERR_GameMode::current=nullptr;
|
|
if(fault==1)f.mode.active=false;
|
|
if(fault==2)game.world=2;
|
|
if(fault==3)SCR_InventoryMenuUI::current=&other;
|
|
if(fault==4)f.handler.m_MenuHandler=&other;
|
|
if(fault==5)f.child.name="Different";
|
|
if(fault==6)f.menu.root=&foreignRoot;
|
|
if(fault==7){foreignGrid.name="OtherGrid";foreignGrid.AddChild(&f.child);}
|
|
f.attach();assert(game.workspace.creates==0&&!f.menu.m_ERRStorageLayoutBound&&f.handler.invalidLayoutCalls==2);
|
|
}
|
|
{Fixture f;Widget named;named.name="ERR_InventoryStorageLayout";f.grid.AddChild(&named);f.attach();assert(game.workspace.creates==0&&f.child.parent==&f.grid&&!named.removed);}
|
|
for(int fault=0;fault<3;fault++){Fixture f;game.workspace.fail=fault==0;game.workspace.wrongType=fault==1;game.workspace.refuseAdd=fault==2;f.attach();assert(f.child.parent==&f.grid&&!f.menu.m_ERRStorageLayoutHost&&!f.menu.m_ERRStorageLayoutBound&&f.handler.invalidLayoutCalls==2);assert(f.child.row==2&&f.child.col==3&&f.child.l==1);if(game.workspace.last)assert(game.workspace.last->removed);}
|
|
{Fixture f;f.attach();auto host=f.menu.m_ERRStorageLayoutHost;f.child.RemoveFromHierarchy();Widget foreign;host->AddChild(&foreign);Widget next;next.name="Frame0";f.grid.AddChild(&next);assert(!f.menu.ERR_PrepareStorageLayout(&next));assert(next.parent==&f.grid&&foreign.parent==host&&game.workspace.creates==1);}
|
|
{Fixture f;f.attach();auto host=f.menu.m_ERRStorageLayoutHost;f.child.RemoveFromHierarchy();host->row=7;host->l=9;host->refuseAdd=true;Widget next;next.name="Frame0";f.grid.AddChild(&next);next.row=6;next.l=8;assert(!f.menu.ERR_PrepareStorageLayout(&next));assert(next.parent==&f.grid&&next.row==6&&next.l==8&&host->row==7&&host->l==9&&game.workspace.creates==1);}
|
|
{Fixture f;f.attach();f.child.RemoveFromHierarchy();Widget replacementRoot;replacementRoot.AddChild(&f.grid);f.menu.root=&replacementRoot;Widget next;next.name="Frame0";f.grid.AddChild(&next);assert(!f.menu.ERR_PrepareStorageLayout(&next));assert(game.workspace.creates==1&&next.parent==&f.grid);}
|
|
{Fixture f;f.attach();auto host=f.menu.m_ERRStorageLayoutHost;f.child.RemoveFromHierarchy();host->RemoveFromHierarchy();f.menu.m_ERRStorageLayoutHost=nullptr;Widget next;next.name="Frame0";f.grid.AddChild(&next);assert(!f.menu.ERR_PrepareStorageLayout(&next));assert(game.workspace.creates==1);}
|
|
{ERR_InventoryGridPlacement p;Widget child;assert(!p.Capture(&child)&&!p.Apply(&child));}
|
|
}
|
|
''')
|
|
s=P.read_text();handler=body(P,'override void HandlerAttached(')
|
|
assert handler.count('super.HandlerAttached(w)')==1
|
|
assert 'return' not in handler
|
|
for forbidden in ('Init(', 'RefreshList(', 'SetFillWeight(', 'SetSizeMode(', 'CallLater(', 'RemoveHandler(', 'CreateWidgets(', 'OpenMenu('):assert forbidden not in s,forbidden
|
|
assert 'class SCR_InventoryStorageBaseUI' not in s
|
|
print('PASS: actual inventory adapter scope, grid placement, sole compatible host, refresh reuse, rollback, foreign hierarchy refusal and exactly-once native delegation; native compatibility pending')
|