Files
EscapistsReintroduced/docs/evidence/vehicle-roles-2026-11-06/final-run/fixture.c.txt
T
jakub 460d9808e5
continuous-integration/drone/push Build is passing
Simplify content controls and add vehicle roles; record Workshop 0.0.144
Discover installed mod vehicles and save independent garage, traffic, patrol and convoy assignments. Retire the superseded RHS 1.9 overrides. Include native menu, crew/driving and publication evidence.
2026-11-06 22:06:38 +01:00

304 lines
16 KiB
Plaintext

#ifdef WORKBENCH
modded class SCR_RespawnSystemComponent
{
override void UpdateLoadingPlaceholder(float dt)
{
super.UpdateLoadingPlaceholder(dt);
if (ERR_AdminMenu.GetOpenMenu() && m_wLoadingPlaceholder) m_wLoadingPlaceholder.SetVisible(false);
}
}
modded class ERR_GameMode
{
protected override void StartScenario() {}
protected override void EOnInit(IEntity owner)
{
super.EOnInit(owner);
if (SCR_Global.IsEditMode() || !Replication.IsServer()) return;
GetGame().GetCallqueue().CallLater(ERR_ContentReviewBegin, 5000, false);
}
protected void ERR_ContentReviewBegin()
{
PrintFormat("ERR CONTENT: controller=%1",GetGame().GetPlayerController()!=null);
ERR_AdminMenu.Open();
}
}
modded class ERR_AdminMenu
{
protected int m_ContentReviewStage;
protected float m_ContentReviewAt;
protected string m_ContentReviewItem;
protected string m_ContentReviewVehicle;
protected string m_ContentReviewAddon="58d0fb3206b6f859";
override void OnMenuOpen()
{
super.OnMenuOpen();
GetGame().GetCallqueue().CallLater(ERR_ContentReviewTick,1000,true);
}
override void OnMenuClose()
{
GetGame().GetCallqueue().Remove(ERR_ContentReviewTick);
super.OnMenuClose();
}
void ERR_ContentReviewFail(string reason)
{
Print("ERR SMOKE FAIL: content menu "+reason,LogLevel.ERROR);
GetGame().GetCallqueue().Remove(ERR_ContentReviewTick);
}
void ERR_ContentReviewTick()
{
float now=GetGame().GetWorld().GetWorldTime();
if(!m_Ready || now<m_ContentReviewAt)return;
m_ContentReviewAt=now+3000;
PrintFormat("ERR CONTENT STAGE: %1 page=%2 vehicles=%3 addon=%4",m_ContentReviewStage,m_Page,m_VehicleRows.Count(),m_ShownVehicleAddon);
ERR_ItemPreferences prefs=GetGame().ERR_ItemSelections();
if(m_ContentReviewStage==0)
{
if(m_ServerAddonKeys.IsEmpty() || m_VehicleMods.IsEmpty())return;
if(!GetGame().ERR_ServerVehicles().Contains("2528466de220d91c","f001c0de00000003"))return;
for(int checkPage=0;checkPage<Pages().Count();checkPage++){ShowPage(checkPage);if(!ERR_ProbeNavigation(checkPage)){ERR_ContentReviewFail("final page bounds");return;}}
ERR_VehicleRolePhysicalProbe.Begin();GetGame().GetCallqueue().Remove(ERR_ContentReviewTick);return;
ShowPage(ERR_AdminPage.MODS);GetRootWidget().Update();
if(!ERR_ProbeNavigation(ERR_AdminPage.MODS)){ERR_ContentReviewFail("navigation");return;}
PrintFormat("ERR CONTENT MODS: rows=%1 vehicleMods=%2",m_ServerAddonRows.Count(),m_VehicleMods.Count());
System.MakeScreenshot("$logs:content-mods");
m_ContentReviewStage=1;return;
}
if(m_ContentReviewStage==1)
{
SCR_ComboBoxComponent.GetComboBoxComponent("ItemsMod",GetRootWidget()).SetCurrentItem(m_ServerAddonKeys.Find(m_ContentReviewAddon),false,false,false);
ShowPage(ERR_AdminPage.ALLOWED_ITEMS);BrowseItems(0);m_ContentReviewStage=2;return;
}
if(m_ContentReviewStage==2)
{
if(m_ItemRows.IsEmpty())return;
foreach(string key,Widget row:m_ItemRows){m_ContentReviewItem=key;OnItemRowButton(SCR_ButtonTextComponent.GetButtonText("Disable",row));break;}
System.MakeScreenshot("$logs:content-items");m_ContentReviewStage=3;return;
}
if(m_ContentReviewStage==3)
{
if(prefs.Enabled(m_ContentReviewAddon,m_ContentReviewItem)){ERR_ContentReviewFail("item disable through menu");return;}
SubmitItems("items mod "+m_ContentReviewAddon+" 0");m_ContentReviewStage=4;return;
}
if(m_ContentReviewStage==4)
{
if(prefs.ModEnabled(m_ContentReviewAddon)){ERR_ContentReviewFail("mod disable");return;}
array<ResourceName> disabledPool={ERR_CivilianTraffic.CarPrefab(0)};
if(!ERR_VehicleSelection.Choose(disabledPool).IsEmpty()){ERR_ContentReviewFail("disabled mod still permits traffic candidate");return;}
SubmitItems("items mod "+m_ContentReviewAddon+" 1");m_ContentReviewStage=5;return;
}
if(m_ContentReviewStage==5)
{
if(!prefs.ModEnabled(m_ContentReviewAddon)||prefs.Enabled(m_ContentReviewAddon,m_ContentReviewItem)){ERR_ContentReviewFail("master restore lost item choice");return;}
SCR_EditBoxComponent.GetEditBoxComponent("ItemsSearch",GetRootWidget()).SetValue("err_no_such_item_9387");BrowseItems(0);m_ContentReviewStage=6;return;
}
if(m_ContentReviewStage==6)
{
if(!m_ItemRows.IsEmpty()){ERR_ContentReviewFail("search stale rows");return;}
Print("ERR SMOKE PASS: real menu item disable, master mod restoration and empty search");
PrintFormat("ERR CONTENT VEHICLE INDEX: %1",m_VehicleMods.Find(m_ContentReviewAddon));
ShowPage(ERR_AdminPage.WEIGHTS); SubmitItems("items list "+m_ContentReviewAddon+" 0");m_ContentReviewStage=10;return;
}
if(m_ContentReviewStage==10)
{
ShowPage(ERR_AdminPage.ALLOWED_ITEMS);m_ContentReviewStage=11;return;
}
if(m_ContentReviewStage==11)
{
if(!m_ItemRows.IsEmpty()){ERR_ContentReviewFail("search lost after advanced page");return;}
Print("ERR SMOKE PASS: item search restored after visiting advanced weights");
OpenVehicleAddon(m_ContentReviewAddon);m_ContentReviewStage=7;return;
}
if(m_ContentReviewStage==7)
{
SCR_ComboBoxComponent enabledFilter=SCR_ComboBoxComponent.GetComboBoxComponent("VehiclesFilter",GetRootWidget());
if(enabledFilter.GetCurrentIndex()!=1){enabledFilter.SetCurrentItem(1,false,false,false);BrowseVehicles(0);return;}
if(m_VehicleRows.IsEmpty())return;
if(!ERR_ProbeNavigation(ERR_AdminPage.VEHICLE_POOL)){ERR_ContentReviewFail("vehicle navigation");return;}
foreach(string key,Widget row:m_VehicleRows){m_ContentReviewVehicle=key;OnVehicleRowButton(SCR_ButtonTextComponent.GetButtonText("Disable",row));break;}
System.MakeScreenshot("$logs:content-vehicles");m_ContentReviewStage=8;return;
}
if(m_ContentReviewStage==8)
{
if(prefs.VehicleRole(m_ContentReviewAddon,m_ContentReviewVehicle,0)!=0){ERR_ContentReviewFail("vehicle disable");return;}
ERR_ServerAddonItems vehicles=GetGame().ERR_ServerVehicles().Find(m_ContentReviewAddon);
foreach(ERR_ServerItem vehicle:vehicles.Items)
if(ERR_TerrainCatalog.Key(vehicle.Prefab)==m_ContentReviewVehicle)
{
array<ResourceName> pool={vehicle.Prefab};
if(ERR_VehicleSelection.RoleAllowed(pool[0],0)){ERR_ContentReviewFail("disabled vehicle still selected");return;}
}
ERR_ItemPreferences reloaded=new ERR_ItemPreferences();
if(reloaded.VehicleRole(m_ContentReviewAddon,m_ContentReviewVehicle,0)!=0||reloaded.Enabled(m_ContentReviewAddon,m_ContentReviewItem)||!reloaded.ModEnabled(m_ContentReviewAddon)){ERR_ContentReviewFail("saved configuration reload");return;}
SCR_ComboBoxComponent.GetComboBoxComponent("VehiclesFilter",GetRootWidget()).SetCurrentItem(2,false,false,false);BrowseVehicles(0);m_ContentReviewStage=9;return;
}
if(m_ContentReviewStage==12)
{
SCR_ComboBoxComponent.GetComboBoxComponent("VehiclesMod",GetRootWidget()).SetCurrentItem(m_VehicleMods.Find("2528466de220d91c"),false,false,false);
SCR_ComboBoxComponent.GetComboBoxComponent("VehiclesRole",GetRootWidget()).SetCurrentItem(2,false,false,false);
SCR_ComboBoxComponent.GetComboBoxComponent("VehiclesFilter",GetRootWidget()).SetCurrentItem(0,false,false,false);
SCR_EditBoxComponent.GetEditBoxComponent("VehiclesSearch",GetRootWidget()).SetValue("RoleTestCar");
ShowPage(ERR_AdminPage.VEHICLE_POOL);BrowseVehicles(0);m_ContentReviewStage=13;return;
}
if(m_ContentReviewStage==13)
{
Widget row=m_VehicleRows.Get("f001c0de00000001");
if(!row||!VehicleSelectionReady()||m_ShownVehicleRole!=2){ERR_ContentReviewFail("role browser selected wrong result");return;}
OnVehicleRowButton(SCR_ButtonTextComponent.GetButtonText("Enable",row));m_ContentReviewStage=14;return;
}
if(m_ContentReviewStage==14)
{
if(!GetGame().ERR_ServerVehicles().Allowed(ERR_VehicleRolePhysicalProbe.Car(),2)||GetGame().ERR_ServerVehicles().Allowed(ERR_VehicleRolePhysicalProbe.Car(),0)){ERR_ContentReviewFail("per-use enabling through menu");return;}
SCR_ComboBoxComponent.GetComboBoxComponent("VehiclesRole",GetRootWidget()).SetCurrentItem(3,false,false,false);
if(VehicleSelectionReady()){ERR_ContentReviewFail("stale role buttons accepted");return;}
BrowseVehicles(0);m_ContentReviewStage=15;return;
}
if(m_ContentReviewStage==15)
{
ERR_ItemPreferences reloadRoles=new ERR_ItemPreferences();
if(m_ShownVehicleRole!=3||reloadRoles.VehicleRole("2528466de220d91c","f001c0de00000001",2)!=1||reloadRoles.VehicleRole("2528466de220d91c","f001c0de00000001",3)!=-1){ERR_ContentReviewFail("independent roles/save reload");return;}
Print("ERR SMOKE PASS: real menu assigns a discovered vehicle to patrols, preserves independent uses, blocks stale role actions and reloads saved assignments");
SubmitItems("vehicles role 2528466de220d91c 2 f001c0de00000001 0");m_ContentReviewStage=16;return;
}
if(m_ContentReviewStage==16)
{
ERR_VehicleRolePhysicalProbe.Begin();GetGame().GetCallqueue().Remove(ERR_ContentReviewTick);return;
}
if(m_ContentReviewStage==9)
{
if(m_VehicleRows.IsEmpty()){ERR_ContentReviewFail("disabled vehicle filter");return;}
Print("ERR SMOKE PASS: vehicle disable, filtered list and persisted preferences reload");
for(int page=0;page<Pages().Count();page++)
{
ShowPage(page);
if(!ERR_ProbeNavigation(page)){ERR_ContentReviewFail("page bounds "+page.ToString());return;}
}
Print("ERR SMOKE PASS: all navigation pages fit and disabled vehicles are excluded from selection");
ShowPage(ERR_AdminPage.MODS);System.MakeScreenshot("$logs:content-final");
m_ContentReviewStage=12;return;
GetGame().GetCallqueue().Remove(ERR_ContentReviewTick);
}
}
}
#endif
#ifdef WORKBENCH
modded class ERR_ConvoyService
{
bool ERR_RoleTestCrew(ERR_ConvoyVehicle unit) { return FillCrew(unit); }
}
modded class ERR_HunterService
{
bool ERR_RoleTestHunter(ERR_HunterVehicle unit) { return FillCrew(unit); }
}
class ERR_VehicleRolePhysicalProbe
{
static ref array<ref ERR_ConvoyVehicle> Units = {};
static ref array<vector> Starts = {};
static float Began;
static bool Failed;
static ResourceName Car() { return "{F001C0DE00000001}Prefabs/Vehicles/ERR_RoleTestCar.et"; }
static ResourceName Escort() { return "{F001C0DE00000002}Prefabs/Vehicles/ERR_RoleTestEscort.et"; }
static ResourceName Truck() { return "{F001C0DE00000003}Prefabs/Vehicles/ERR_RoleTestTruck.et"; }
static void Fail(string message)
{
Failed = true; Print("ERR SMOKE FAIL: vehicle roles " + message, LogLevel.ERROR);
GetGame().GetCallqueue().Remove(Tick);
}
static void Begin()
{
ERR_GameMode mode = ERR_GameMode.Get();
ERR_ServerVehicleCatalog catalog = GetGame().ERR_ServerVehicles();
ERR_ItemPreferences preferences = GetGame().ERR_ItemSelections();
string addon = "2528466de220d91c";
array<ResourceName> prefabs = {Car(), Escort(), Truck()};
array<int> roles = {2, 3, 4};
ERR_ConvoyService convoy = new ERR_ConvoyService(mode, null);
ERR_HunterService hunterService = new ERR_HunterService(mode, null);
for (int i = 0; i < 3; i++)
{
ResourceName prefab = prefabs[i]; int role = roles[i]; string key = ERR_TerrainCatalog.Key(prefab);
PrintFormat("ERR ROLE FOUND: %1 contains=%2 capable=%3 sources=%4",prefab,catalog.Contains(addon,key),catalog.Capable(key,role),SCR_AddonTool.GetResourceAddons(prefab));
Resource inspected=Resource.Load(prefab);IEntitySource source=SCR_BaseContainerTools.FindEntitySource(inspected);
if(source)for(int c=0;c<source.GetComponentCount();c++)PrintFormat("ERR ROLE COMPONENT: %1",source.GetComponent(c).GetClassName());
if (!catalog.Contains(addon,key) || !catalog.Capable(key,role)) { Fail("uncatalogued mod prefab not discovered " + prefab); return; }
if (catalog.Allowed(prefab,role)) { Fail("new vehicle unexpectedly enabled by default"); return; }
if(i==0)
{
if(catalog.Allowed(prefab,0)||catalog.Allowed(prefab,1)){Fail("new civilian vehicle enabled before assignment");return;}
if(!preferences.SetVehicleRole(addon,key,0,true)||!preferences.SetVehicleRole(addon,key,1,true)||catalog.Choose(0,"",false)!=prefab||catalog.Choose(1,"")!=prefab){Fail("new civilian vehicle not selected for garage and traffic");return;}
Print("ERR SMOKE PASS: previously unknown car assigned independently to garage and traffic selections");
}
if (!preferences.SetVehicleRole(addon,key,role,true) || !catalog.Allowed(prefab,role)) { Fail("role assignment did not enable candidate"); return; }
bool picked;
for (int sample = 0; sample < 100; sample++) if (catalog.Choose(role, "") == prefab) picked = true;
if (!picked) { Fail("assigned mod prefab not in spawn selection"); return; }
vector pos, angles;
vector seed = Vector(6000 + i * 300, 0, 6500);
bool placed;
ERR_ConvoyPlacement placement = new ERR_ConvoyPlacement();
for (int attempt = 0; attempt < 40; attempt++)
{
if (ERR_SupplyConvoySystem.ApproachDirected(seed + Vector(attempt * 30,0,attempt * 20),pos,angles) && placement.Clear(pos,angles[0])) { placed=true; break; }
}
if (!placed) { Fail("no test road placement"); return; }
ERR_ConvoyVehicle unit;
if (i == 0)
{
ERR_HunterVehicle hunter = new ERR_HunterVehicle(); hunter.AssignedVehicle = true; unit = hunter;
}
else unit = new ERR_ConvoyVehicle();
unit.Index = 0; if(i==2)unit.Index=1;
unit.Entity = Vehicle.Cast(ERR_Actors.Spawn(prefab,pos,angles));
unit.Group = SCR_AIGroup.Cast(ERR_Actors.Spawn(ERR_ConvoyResources.GroupPrefab(),pos));
if(unit.Entity)mode.Own(unit.Entity);if(unit.Group)mode.Own(unit.Group);
if(!unit.Entity || !unit.Group || !ERR_VehicleSelection.RoadFit(unit.Entity)){Fail("physical road fit " + prefab);return;}
if(i==0){vector low,high;unit.Entity.GetBounds(low,high);if(!ERR_GarageVehicleRules.Envelope(low,high)){Fail("new car garage footprint");return;}}
ERR_Actors.SetFaction(unit.Entity,mode.EnemyFaction());unit.Group.SetFaction(mode.EnemyFaction());
bool crew;
if(i==0)crew=hunterService.ERR_RoleTestHunter(ERR_HunterVehicle.Cast(unit));
else crew=convoy.ERR_RoleTestCrew(unit);
if(!crew){Fail("production crew allocation " + prefab);return;}
Units.Insert(unit);Starts.Insert(pos);
PrintFormat("ERR SMOKE PASS: discovered assigned vehicle role=%1 prefab=%2 actualCrew=%3",role,prefab,unit.Members.Count());
if(i==2 && !unit.Entity.FindComponent(InventoryStorageManagerComponent)){Fail("supply inventory absent");return;}
Resource resource=Resource.Load(prefab);
IEntityComponentSource compartments=SCR_BaseContainerTools.FindComponentSource(resource,SCR_BaseCompartmentManagerComponent);
if(compartments)for(int v=0;v<compartments.GetNumVars();v++)PrintFormat("ERR ROLE COMPARTMENT VAR: %1",compartments.GetVarName(v));
}
Began=GetGame().GetWorld().GetWorldTime();
GetGame().GetCallqueue().CallLater(Tick,1000,true);
}
static void Tick()
{
float now=GetGame().GetWorld().GetWorldTime();
bool movedAll=true;
for(int i=0;i<Units.Count();i++)
{
ERR_ConvoyVehicle unit=Units[i];
if(!unit.Registered)
{
SCR_AIGroupUtilityComponent utility=unit.Group.GetGroupUtilityComponent();
SCR_AIVehicleUsageComponent usage=SCR_AIVehicleUsageComponent.Cast(unit.Entity.FindComponent(SCR_AIVehicleUsageComponent));
if(!utility || !utility.m_VehicleMgr || !usage || !utility.m_VehicleMgr.TryAddVehicle(usage)){movedAll=false;continue;}
utility.AddUsableVehicle(usage);unit.Group.SetPermanentLOD(0);unit.Group.ActivateAI();
array<AIAgent> agents={};unit.Group.GetAgents(agents);
foreach(AIAgent agent:agents){agent.SetPermanentLOD(0);agent.ActivateAI();}
vector target,angles;vector transform[4];unit.Entity.GetTransform(transform);
if(!ERR_SupplyConvoySystem.ApproachDirected(Starts[i]+transform[2]*150,target,angles)){movedAll=false;continue;}
unit.Waypoint=AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.MoveWaypoint(),target));
if(!unit.Waypoint){Fail("move waypoint");return;}
ERR_GameMode.Get().Own(unit.Waypoint);unit.Group.AddWaypoint(unit.Waypoint);unit.Registered=true;
}
float distance=vector.DistanceXZ(Starts[i],unit.Entity.GetOrigin());
if(distance<20)movedAll=false;
if(now-Began>120000){Fail("driving timeout role="+i.ToString()+" distance="+distance.ToString());return;}
}
if(!movedAll)return;
Print("ERR SMOKE PASS: newly discovered patrol, escort and supply vehicles physically driven more than20m with production crews");
Print("ERR SMOKE COMPLETE: vehicle roles discovery assignments native crews and driving");
GetGame().GetCallqueue().Remove(Tick);
}
}
#endif