Add civilian traffic variants, farm tractors and UI scaling; record Workshop 0.0.143
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-11-06 12:37:08 +01:00
parent 55c0ec775a
commit 518a1a5744
30 changed files with 4563 additions and 44 deletions
+3 -1
View File
@@ -65,7 +65,9 @@ class MenuSourceTests(unittest.TestCase):
for key in ('PositionX', 'PositionY', 'SizeX', 'SizeY')]
nav = json.loads((builder.SOURCE / 'navigation.json').read_text())
pages = {p['key']: p for p in nav['pages']}
panel_width, panel_height = rect('Panel')[2:]
panel = re.search(r'Name "PanelSize" ([^{}]+)', layout)[1]
panel_width = int(re.search(r'\bWidthOverride (\d+)', panel)[1])
panel_height = int(re.search(r'\bHeightOverride (\d+)', panel)[1])
previous_right = 0
for group in nav['groups']:
x, y, width, height = rect('Main_' + group['name'])
+34
View File
@@ -0,0 +1,34 @@
"""Execute panel-only scaling with viewport and DPI boundaries."""
from policy_harness import ROOT, body, run
source=body(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_AdminMenuView.c','static void FitPanel(')
source=source.replace('Widget root','Widget* root').replace('ScaleWidget scale = ScaleWidget.Cast(root.FindAnyWidget("PanelScale"));','ScaleWidget* scale = root->FindAnyWidget("PanelScale");')
source=source.replace('GetGame().','GetGame()->').replace('GetWorkspace().','GetWorkspace()->').replace('root.','root->').replace('scale.','scale->').replace('Math.','Math::')
run(r'''
#include <algorithm>
#include <cassert>
#include <cmath>
struct ScaleWidget {float scale=1;int writes=0;float GetScale(){return scale;}void SetScale(float v){scale=v;writes++;}};
struct Widget {float width=1920,height=1080;ScaleWidget panel;bool present=true;ScaleWidget* FindAnyWidget(const char*){return present?&panel:nullptr;}void GetScreenSize(float& w,float& h){w=width;h=height;}};
struct Workspace {float factor=1;float DPIScale(float x){return x*factor;}} workspace;
struct Game {Workspace* GetWorkspace(){return &workspace;}} game;
Game* GetGame(){return &game;}
namespace Math {float Min(float a,float b){return std::min(a,b);}float AbsFloat(float v){return std::abs(v);}}
'''+source+r'''
int main(){
Widget root;FitPanel(nullptr);FitPanel(&root);assert(root.panel.scale==1 && root.panel.writes==0);
for(float dpi:{.75f,1.f,1.25f,1.5f,2.f}) {
workspace.factor=dpi;FitPanel(&root);
assert(root.panel.scale<=1 && root.panel.scale>0);
assert(1220*dpi*root.panel.scale<=root.width*.96f+.1f);
assert(760*dpi*root.panel.scale<=root.height*.96f+.1f);
int writes=root.panel.writes;FitPanel(&root);assert(root.panel.writes==writes);
assert(workspace.factor==dpi);
}
root.width=800;root.height=600;FitPanel(&root);
assert(1220*workspace.factor*root.panel.scale<=768.1f);
root.width=3840;root.height=2160;workspace.factor=1;FitPanel(&root);assert(root.panel.scale==1);
root.height=0;int writes=root.panel.writes;FitPanel(&root);assert(root.panel.writes==writes);
root.present=false;FitPanel(&root);assert(root.panel.writes==writes);
}
''')
print('PASS: panel fits narrow/large-DPI viewports, restores requested size, avoids repeated writes and preserves user DPI')
+2 -2
View File
@@ -36,7 +36,7 @@ namespace ERR_CivilianTrafficRules{
bool CleanupDue(float now,float since){return since>=0&&now-since>=60000;}
}
struct SCR_GarbageSystem{static SCR_GarbageSystem* GetByEntityWorld(IEntity*){static SCR_GarbageSystem g;return &g;}void UpdateBlacklist(IEntity*,bool){}};
struct SiteData{bool Consumed=false;};
struct SiteData{bool Consumed=false;bool Farm=false;};
struct ERR_ParkedVehicle{IEntity* Entity=nullptr;SiteData* Site=nullptr;Vec Origin;float HiddenSince=-1;};
struct Mode{int owned=0;void Own(IEntity*){owned++;}};
struct Service{
@@ -46,7 +46,7 @@ struct Service{
Array<ERR_ParkedVehicle*> m_Parked;Array<Vec> views;Array<IEntity*> actors;
bool Running(){return running;}float Now(){return clock;}
bool Observers(Array<Vec>& o,Array<IEntity*>& a){o=views;a=actors;return known;}
bool Hidden(Vec,Vec,Array<IEntity*>& a,IEntity* e){hiddenCalls++;return e->hidden;}
bool Hidden(Vec,Vec,Array<IEntity*>& a,IEntity* e,bool farm){hiddenCalls++;return e->hidden;}
void Discover(Array<Vec>&,float){discovers++;}void Plan(Array<Vec>&,Array<IEntity*>&,float){plans++;}
void Pending(Array<Vec>&,Array<IEntity*>&){pending++;}
''' + methods.replace('m_Pending = nullptr','m_Pending = 0') + r'''
+19 -4
View File
@@ -1,8 +1,9 @@
"""Run production garage filtering and vehicle-fit rules against unsafe inputs."""
from policy_harness import ROOT, body, run
p = ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_GarageVehicles.c'
rules = '\n'.join(body(p, s) for s in ('static bool Garage(', 'static bool Envelope(', 'static bool Yard(', 'static vector YardOffset('))
rules = '\n'.join(body(p, s) for s in ('static bool Garage(', 'static bool Envelope(', 'static bool Yard(', 'static bool Farm(', 'static bool FarmEnvelope(', 'static vector YardOffset('))
rules += '\n' + body(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_ParkedCarCatalog.c', 'static bool Candidate(')
rules += '\n' + body(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_ParkedCarCatalog.c', 'static bool MovingCandidate(')
rules = rules.replace('vector ', 'Vec ').replace('ERR_ForceRules.', 'ERR_ForceRules::')
run(r'''
#include <cassert>
@@ -32,12 +33,26 @@ int main(){
assert(!Yard("Prefabs/Structures/Houses/Village/House_Village_E_1I06/House_Village_E_1I06_base.et"));
assert(!Yard("Prefabs/Structures/Houses/Village/House_Village_E_1I06/Dst/House_Village_E_1I06_ruin.et"));
assert(!Yard("Prefabs/Structures/Houses/Village/House_Village_E_1I06/Furniture/House_Village_E_1I06_furniture_01.et"));
assert(Farm("Prefabs/Structures/Agriculture/Barn/Barn_E_01/Barn_E_01_white.et"));
assert(Farm("Prefabs/Structures/Agriculture/CowShed/CowShed_E_01/CowShed_E_01_short.et"));
assert(!Farm("Prefabs/Structures/Agriculture/Barn/Barn_E_01/Dst/Barn_E_01_Ruin.et"));
assert(!Farm("Prefabs/Structures/Agriculture/Barn/Barn_E_01/Barn_E_01_Base.et"));
assert(!Farm("Prefabs/Structures/Agriculture/Barn/Barn_E_01/Furniture/Barn_E_01_furniture_01.et"));
assert(FarmEnvelope({{-1.4,-0.2,-2.9}},{{1.4,3,2.9}}));
assert(!FarmEnvelope({{-1.5,0,-2}},{{1,3,2}}));
assert(!FarmEnvelope({{-1,0,-2}},{{1,3.2,2}}));
assert(!FarmEnvelope({{-1,0,-3}},{{1,3,2}}));
assert(!FarmEnvelope({{-1,0,-2}},{{1,NAN,2}}));
Vec low={{-4,-1,-6}},high={{6,8,8}};
assert(YardOffset(low,high,0)[0]==-7 && YardOffset(low,high,0)[2]==1);
assert(YardOffset(low,high,1)[0]==9 && YardOffset(low,high,1)[1]==0);
assert(YardOffset(low,high,2)[2]==-9 && YardOffset(low,high,2)[0]==1);
assert(YardOffset(low,high,3)[2]==11);
assert(YardOffset(low,high,0,4)[0]==-8 && YardOffset(low,high,3,4)[2]==12);
assert(Candidate("{GUID}Prefabs/Vehicles/Wheeled/T601/T601_blue_twoTone.et",false));
assert(!MovingCandidate("{GUID}Prefabs/Vehicles/Wheeled/T601/T601_blue_twoTone.et"));
assert(MovingCandidate("{GUID}Prefabs/Vehicles/Wheeled/V2101/V2101_lightBlue.et"));
assert(!MovingCandidate("Prefabs/Vehicles/Wheeled/S105/S105_rally_08.et"));
assert(Candidate("Prefabs/Vehicles/Wheeled/V2101/V2101_lightBlue.et",false));
assert(Candidate("Prefabs/Vehicles/Wheeled/S105/S105_rally_08.et",true));
assert(!Candidate("Prefabs/Vehicles/Wheeled/S105/S105_rally_08.et",false));
@@ -75,13 +90,13 @@ struct Vec {float v[3];float operator[](int i)const{return v[i];}
Vec operator+(Vec b)const{return {{v[0]+b[0],v[1]+b[1],v[2]+b[2]}};}
Vec operator*(float n)const{return {{v[0]*n,v[1]*n,v[2]*n}};}};
bool worldAvailable=true,managerAvailable=true,hasRoad=true,wetEdge=false;
float roadDistance=20,edgeHeight=0;
float roadDistance=20,edgeHeight=0,waterBoundary=2;
struct BaseRoad{float width=10;float GetWidth(){return width;}} roadInstance;
struct Roads{void GetClosestRoad(Vec,BaseRoad*& road,float& distance,bool){road=hasRoad?&roadInstance:nullptr;distance=roadDistance;}} roads;
struct SCR_AIWorld{Roads* GetRoadNetworkManager(){return managerAvailable?&roads:nullptr;}} nativeWorld;
namespace ERR_ForceRules{bool Finite(float n){return std::isfinite(n);}}
namespace SCR_TerrainHelper{float GetTerrainY(Vec p){return p[0]>.5f?edgeHeight:0;}}
namespace ERR_Actors{bool DryGround(Vec p){return !(wetEdge&&p[2]>2)&&std::isfinite(SCR_TerrainHelper::GetTerrainY(p));}}
namespace ERR_Actors{bool DryGround(Vec p){return !(wetEdge&&p[2]>waterBoundary)&&std::isfinite(SCR_TerrainHelper::GetTerrainY(p));}}
namespace Math{float AbsFloat(float n){return std::abs(n);}}
''' + ground + r'''
int main(){Vec p={{0,0,0}},matrix[4]={{{1,0,0}},{{0,1,0}},{{0,0,1}},{{0,0,0}}};
@@ -90,7 +105,7 @@ int main(){Vec p={{0,0,0}},matrix[4]={{{1,0,0}},{{0,1,0}},{{0,0,1}},{{0,0,0}}};
hasRoad=false;assert(YardGround(p,matrix));hasRoad=true;
worldAvailable=false;assert(!YardGround(p,matrix));worldAvailable=true;
managerAvailable=false;assert(!YardGround(p,matrix));managerAvailable=true;
wetEdge=true;assert(!YardGround(p,matrix));wetEdge=false;
wetEdge=true;assert(!YardGround(p,matrix));waterBoundary=2.75f;assert(YardGround(p,matrix)&&!YardGround(p,matrix,true));wetEdge=false;
edgeHeight=.19f;assert(!YardGround(p,matrix));edgeHeight=.17f;assert(YardGround(p,matrix));
edgeHeight=NAN;assert(!YardGround(p,matrix));edgeHeight=0;
roadDistance=NAN;assert(!YardGround(p,matrix));