Add configurable nearby garage vehicles and fix roadblocks and fuel pumps; record Workshop 0.0.131
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-10-30 18:56:57 +01:00
parent 3ec882dc4e
commit 2dcbe3107d
75 changed files with 7478 additions and 59 deletions
@@ -3,7 +3,7 @@ class ERR_AdminMenuFields
{
static array<string> Keys()
{
return {"aiDoctrine", "aiShadow", "automaticEquipmentChancePercent", "sarkaAngerPerCar", "motorbikeCount", "droneReconCount", "droneCooldownSeconds", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "emptyRestartHours", "startMode", "radioDefenceSeconds", "aiIntelChancePercent", "intelRevealChancePercent", "radioIntelSiteCount", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "convoyCount", "antiAirChancePercent", "antiPersonnelMineChancePercent", "heliTimerMinutes"};
return {"aiDoctrine", "aiShadow", "automaticEquipmentChancePercent", "sarkaAngerPerCar", "motorbikeCount", "droneReconCount", "droneCooldownSeconds", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "garageVehicleCount", "garageChancePercent", "garageRadiusMetres", "garageSpawnIntervalSeconds", "emptyRestartHours", "startMode", "radioDefenceSeconds", "aiIntelChancePercent", "intelRevealChancePercent", "radioIntelSiteCount", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "convoyCount", "antiAirChancePercent", "antiPersonnelMineChancePercent", "heliTimerMinutes"};
}
static bool Available(string key, bool hq, bool intel)
{
@@ -64,6 +64,10 @@ class ERR_AdminMenuFields
else if (key == "fieldHospitalCount") prefix = "mission hospitals";
else if (key == "roadblockCount") prefix = "mission roadblocks";
else if (key == "emptyRestartHours") prefix = "mission emptyhours";
else if (key == "garageChancePercent") prefix = "mission garagechance";
else if (key == "garageRadiusMetres") prefix = "mission garageradius";
else if (key == "garageSpawnIntervalSeconds") prefix = "mission garageinterval";
else if (key == "garageVehicleCount") prefix = "mission garagevehicles";
else if (key == "civilianTrafficCount") prefix = "mission traffic";
else if (key == "startMode") prefix = "mission start";
else if (key == "radioDefenceSeconds") prefix = "mission radio";
@@ -26,17 +26,18 @@ enum ERR_AdminPage
DRONES = 22,
RADIO_INTEL = 23,
TELEMETRY = 24,
VEHICLES = 25,
}
class ERR_AdminMenuPages
{
static array<string> Pages() { return {"SettingsPage", "HQPage", "JournalPage", "ModsPage", "FactionsPage", "WorldPage", "IntelPage", "StatusPage", "TerrainsPage", "ItemsPage", "TTSPage", "ForcesPage", "GuardsPage", "SitesPage", "PlanePage", "RecommendationsPage", "WeightsPage", "AllowedItemsPage", "ItemPresetsPage", "AIPage", "StarterPage", "SarkaPage", "DronesPage", "RadioIntelPage", "TelemetryPage"}; }
static array<string> Navigation() { return {"OpenSettings", "OpenHQ", "OpenJournal", "OpenMods", "OpenFactions", "OpenWorld", "OpenIntel", "OpenStatus", "OpenTerrains", "OpenItems", "OpenTTS", "OpenForces", "OpenGuards", "OpenSites", "OpenPlane", "OpenRecommendations", "OpenWeights", "OpenAllowedItems", "OpenItemPresets", "OpenAI", "OpenStarter", "OpenSarka", "OpenDrones", "OpenRadioIntel", "OpenTelemetry"}; }
static array<string> Pages() { return {"SettingsPage", "HQPage", "JournalPage", "ModsPage", "FactionsPage", "WorldPage", "IntelPage", "StatusPage", "TerrainsPage", "ItemsPage", "TTSPage", "ForcesPage", "GuardsPage", "SitesPage", "PlanePage", "RecommendationsPage", "WeightsPage", "AllowedItemsPage", "ItemPresetsPage", "AIPage", "StarterPage", "SarkaPage", "DronesPage", "RadioIntelPage", "TelemetryPage", "VehiclesPage"}; }
static array<string> Navigation() { return {"OpenSettings", "OpenHQ", "OpenJournal", "OpenMods", "OpenFactions", "OpenWorld", "OpenIntel", "OpenStatus", "OpenTerrains", "OpenItems", "OpenTTS", "OpenForces", "OpenGuards", "OpenSites", "OpenPlane", "OpenRecommendations", "OpenWeights", "OpenAllowedItems", "OpenItemPresets", "OpenAI", "OpenStarter", "OpenSarka", "OpenDrones", "OpenRadioIntel", "OpenTelemetry", "OpenVehicles"}; }
static array<string> MainButtons() { return {"Main_Mission", "Main_Forces", "Main_HQ", "Main_Intel", "Main_Items", "Main_Mods", "Main_Audio", "Main_Status"}; }
static array<string> Sidebars() { return {"Sidebar_Mission", "Sidebar_Forces", "Sidebar_HQ", "Sidebar_Intel", "Sidebar_Items", "Sidebar_Mods", "Sidebar_Audio", "Sidebar_Status"}; }
static int Group(int page)
{
array<int> groups = {0, 2, 7, 5, 1, 0, 3, 7, 0, 4, 6, 1, 1, 1, 2, 5, 4, 4, 4, 2, 4, 1, 2, 3, 0};
array<int> groups = {0, 2, 7, 5, 1, 0, 3, 7, 0, 4, 6, 1, 1, 1, 2, 5, 4, 4, 4, 2, 4, 1, 2, 3, 0, 0};
if (page < 0 || page >= groups.Count()) return -1;
return groups[page];
}
@@ -24,6 +24,10 @@ class ERR_CommandArguments
if (section == "mission" && option == "objectguards") return "objectGuardCount";
if (section == "mission" && option == "hunterdistance") return "hunterSpawnDistanceMetres";
if (section == "mission" && option == "roadblocks") return "roadblockCount";
if (section == "mission" && option == "garagechance") return "garageChancePercent";
if (section == "mission" && option == "garageradius") return "garageRadiusMetres";
if (section == "mission" && option == "garageinterval") return "garageSpawnIntervalSeconds";
if (section == "mission" && option == "garagevehicles") return "garageVehicleCount";
if (section == "mission" && option == "traffic") return "civilianTrafficCount";
if (section == "mission" && option == "start") return "startMode";
if (section == "mission" && option == "selfepi") return "selfEpinephrine";
@@ -194,6 +198,10 @@ class ERR_AdminService
if (key == "telemetryEnabled") return "Default on. Sends mission results and player identities to the configured statistics service. Disabling clears unsent events immediately.";
if (key == "restartOnEmpty") return "On (1) restarts after the last disconnect; Off (0) preserves the round for reconnects. Default On. Applies immediately; normal round endings still apply.";
if (key == "emptyRestartHours") return "0 disables idle restarts; 1..168 real hours continuously empty. Default 5. Applies immediately, measured from mission start or the last disconnect. Connected players, including spectators, prevent idle restarts.";
if (key == "garageChancePercent") return "Garage selection chance (0-100%). Default 70%; 100 selects every suitable garage. Next round.";
if (key == "garageRadiusMetres") return "Spawn radius (250-1500 metres). Distance around players; default 1000 m. Next round.";
if (key == "garageSpawnIntervalSeconds") return "Time between spawns (5-60 seconds). Default 5 s. Increase to spread out spawning. Next round.";
if (key == "garageVehicleCount") return "0..24 unclaimed garage vehicles near players; default 16. Used vehicles are kept; distant unused vehicles retire. Optional bikes. Applies next round.";
if (key == "civilianTrafficCount") return "Set 0..6 civilian cars (default 3). Zero disables new traffic. Unclaimed cars retire only far from all players and out of sight; player-entered cars are preserved.";
if (key == "showAllSites") return "0 disabled (default), 1 enabled. Show prepared sites at their positions as question marks; recovered intel identifies their types. Applies immediately to everyone.";
if (key == "selfEpinephrine") return "0 disabled (default), 1 enabled. Unconscious players can press Space to use a carried epinephrine injector. Applies immediately; consumes the item and keeps its normal medical effect.";
@@ -207,7 +215,7 @@ class ERR_AdminService
}
protected string RoadblockTiming(string key)
{
if (key == "radioIntelSiteCount") return " for next round";
if (key == "radioIntelSiteCount" || key == "garageVehicleCount" || key == "garageChancePercent" || key == "garageRadiusMetres" || key == "garageSpawnIntervalSeconds") return " for next round";
if (key == "aiDoctrine" || key == "aiShadow" || key == "droneReconCount" || key == "droneCooldownSeconds") return " for next round";
if (key == "motorbikeCount" || key == "planeAltitudeMetres" || key == "planeReplacementSeconds" || key == "planeSectorSeconds" || key == "planeReconEnabled" || key == "fieldCampCount" || key == "fieldHospitalCount" || key == "startWeaponCount" || key == "startMagazineCount" || key == "objectGuardCount" || key == "objectGuardEvery" || key == "objectGuardHQMode" || key == "hunterSpawnDistanceMetres" || key == "roadblockCount") return " for next round";
return "";
@@ -65,6 +65,7 @@ class ERR_GameMode : SCR_BaseGameMode
protected ref ERR_Performance m_Performance;
ERR_Performance Performance() { return m_Performance; }
protected ref ERR_CivilianTraffic m_CivilianTraffic;
protected ref ERR_GarageVehicles m_GarageVehicles;
protected ref ERR_ForceLedger m_Forces = new ERR_ForceLedger();
protected ref ERR_ForceSimulation m_ForceSimulation;
protected ref array<AIAgent> m_ForceAgents = {};
@@ -143,6 +144,7 @@ class ERR_GameMode : SCR_BaseGameMode
ERR_ForceLedger Forces() { return m_Forces; }
ERR_CommandService Command() { return m_Command; }
ERR_CivilianTraffic CivilianTraffic() { return m_CivilianTraffic; }
ERR_GarageVehicles GarageVehicles() { return m_GarageVehicles; }
ERR_Objectives Objectives() { return m_Objectives; }
ERR_ExtractionService Extraction() { return m_Extraction; }
string EndReason() { return m_EndReason; }
@@ -546,6 +548,9 @@ class ERR_GameMode : SCR_BaseGameMode
m_Command = CreateCommandService();
m_CivilianTraffic = new ERR_CivilianTraffic(this);
OnStop().Insert(m_CivilianTraffic.Stop);
m_GarageVehicles = new ERR_GarageVehicles(this);
OnStop().Insert(m_GarageVehicles.Stop);
m_GarageVehicles.Start();
GetGame().GetCallqueue().CallLater(ServerTick, 1000, true);
GetGame().GetCallqueue().CallLater(StartScenario, 1500, false);
}
@@ -9,7 +9,7 @@ class ERR_Settings
static array<string> Keys()
{
return {"telemetryEnabled", "aiDoctrine", "aiShadow", "sarkaAngerPerCar", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "planeReconEnabled", "droneReconCount", "droneCooldownSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "startMode", "keepWeaponOnUnconscious", "selfEpinephrine", "restartOnEmpty", "emptyRestartHours", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "aiIntelChancePercent", "intelRevealChancePercent", "radioIntelSiteCount", "showAllSites", "radioDefenceSeconds", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "antiAirChancePercent", "antiPersonnelMineChancePercent", "automaticEquipmentChancePercent", "automaticConvoyEnabled", "convoyCount", "heliTimerMinutes"};
return {"telemetryEnabled", "aiDoctrine", "aiShadow", "sarkaAngerPerCar", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "planeReconEnabled", "droneReconCount", "droneCooldownSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "garageVehicleCount", "garageChancePercent", "garageRadiusMetres", "garageSpawnIntervalSeconds", "startMode", "keepWeaponOnUnconscious", "selfEpinephrine", "restartOnEmpty", "emptyRestartHours", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "aiIntelChancePercent", "intelRevealChancePercent", "radioIntelSiteCount", "showAllSites", "radioDefenceSeconds", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "antiAirChancePercent", "antiPersonnelMineChancePercent", "automaticEquipmentChancePercent", "automaticConvoyEnabled", "convoyCount", "heliTimerMinutes"};
}
static bool Valid(string key, int value)
@@ -33,6 +33,10 @@ class ERR_Settings
if (key == "objectGuardCount") return value >= 0 && value <= 16;
if (key == "hunterSpawnDistanceMetres") return value >= 800 && value <= 3000;
if (key == "roadblockCount") return value >= 0 && value <= 30;
if (key == "garageChancePercent") return value >= 0 && value <= 100;
if (key == "garageRadiusMetres") return value >= 250 && value <= 1500;
if (key == "garageSpawnIntervalSeconds") return value >= 5 && value <= 60;
if (key == "garageVehicleCount") return value >= 0 && value <= 24;
if (key == "civilianTrafficCount") return value >= 0 && value <= 6;
if (key == "startMode") return value == 0 || value == 1;
if (key == "showAllSites") return value == 0 || value == 1;
@@ -141,6 +145,10 @@ class ERR_Settings
if (key == "hunterSpawnDistanceMetres") return 1000;
if (key == "roadblockCount") return 4;
if (key == "civilianTrafficCount") return 3;
if (key == "garageChancePercent") return 70;
if (key == "garageRadiusMetres") return 1000;
if (key == "garageSpawnIntervalSeconds") return 5;
if (key == "garageVehicleCount") return 16;
if (key == "showAllSites") return 0;
if (key == "startMode" || key == "selfEpinephrine") return 0;
if (key == "telemetryEnabled" || key == "keepWeaponOnUnconscious" || key == "restartOnEmpty") return 1;
@@ -164,6 +172,10 @@ class ERR_Settings
m_Values.Set("aiDoctrine", AddedDefault("aiDoctrine"));
m_Values.Set("aiShadow", AddedDefault("aiShadow"));
m_Values.Set("sarkaAngerPerCar", AddedDefault("sarkaAngerPerCar"));
m_Values.Set("garageChancePercent", AddedDefault("garageChancePercent"));
m_Values.Set("garageRadiusMetres", AddedDefault("garageRadiusMetres"));
m_Values.Set("garageSpawnIntervalSeconds", AddedDefault("garageSpawnIntervalSeconds"));
m_Values.Set("garageVehicleCount", AddedDefault("garageVehicleCount"));
m_Values.Set("motorbikeCount", AddedDefault("motorbikeCount"));
m_Values.Set("planeAltitudeMetres", AddedDefault("planeAltitudeMetres"));
m_Values.Set("planeReplacementSeconds", AddedDefault("planeReplacementSeconds"));
@@ -0,0 +1,71 @@
// World-authored pumps survive a game-mode takeover. Their native registrations
// belong to the discarded manager and must move to the replacement on each peer.
modded class SCR_SupportStationManagerComponent
{
void ERR_CollectTerrainStations(array<SCR_BaseSupportStationComponent> stations)
{
for (int i = 0; i < m_mSupportStations.Count(); i++)
{
array<SCR_BaseSupportStationComponent> registered = m_mSupportStations.GetElement(i);
if (!registered) continue;
foreach (SCR_BaseSupportStationComponent station : registered)
if (station && !stations.Contains(station)) stations.Insert(station);
}
}
override void OnPostInit(IEntity owner)
{
super.OnPostInit(owner);
if (ERR_TerrainTakeoverMode.Cast(owner) && GetInstance() == this)
GetGame().ERR_RestoreTerrainSupportStations(this);
}
}
modded class SCR_BaseSupportStationComponent
{
bool ERR_CanRestoreTerrainStation()
{
IEntity owner = GetOwner();
return owner && GetGame() && owner.GetWorld() == GetGame().GetWorld()
&& UsesRange() && m_eCurrentDamageState != EDamageState.DESTROYED;
}
void ERR_TerrainStationRestored(SCR_SupportStationManagerComponent manager) {}
}
modded class SCR_BaseItemSupportStationComponent
{
override void ERR_TerrainStationRestored(SCR_SupportStationManagerComponent manager)
{
super.ERR_TerrainStationRestored(manager);
// Item cleanup must remove the station from the manager it now belongs to.
if (m_ERRItemStationInitialized && UsesRange()) m_ERRItemStationManager = manager;
}
}
modded class ArmaReforgerScripted
{
protected ref array<SCR_BaseSupportStationComponent> m_ERRTerrainSupportStations = {};
void ERR_CaptureTerrainSupportStations(SCR_SupportStationManagerComponent original)
{
m_ERRTerrainSupportStations.Clear();
if (original) original.ERR_CollectTerrainStations(m_ERRTerrainSupportStations);
}
void ERR_RestoreTerrainSupportStations(SCR_SupportStationManagerComponent manager)
{
if (!manager) return;
int restored;
foreach (SCR_BaseSupportStationComponent station : m_ERRTerrainSupportStations)
{
if (!station || !station.ERR_CanRestoreTerrainStation()) continue;
manager.AddSupportStation(station);
station.ERR_TerrainStationRestored(manager);
restored++;
}
m_ERRTerrainSupportStations.Clear();
PrintFormat("Reintroduced [terrain support]: retained %1 existing support stations", restored);
}
override protected void OnBeforeWorldCleanup()
{
m_ERRTerrainSupportStations.Clear();
super.OnBeforeWorldCleanup();
}
}
@@ -243,6 +243,7 @@ modded class ArmaReforgerScripted
}
}
Print("Reintroduced [terrain takeover]: stopping original scenario; preserving radio and chat managers");
ERR_CaptureTerrainSupportStations(SCR_SupportStationManagerComponent.Cast(m_ERRTerrainOriginal.FindComponent(SCR_SupportStationManagerComponent)));
m_ERRTerrainOriginal.OnGameEnd();
if (m_DataCollectorComponent && m_DataCollectorComponent.GetOwner() == m_ERRTerrainOriginal) m_DataCollectorComponent = null;
SCR_EntityCatalogManagerComponent.ERR_ReleaseTerrainCatalog(m_ERRTerrainOriginal);
@@ -102,6 +102,7 @@ modded class ArmaReforgerScripted
m_ERRClientTerrainTakeover = true;
SCR_BaseGameMode mode = SCR_BaseGameMode.Cast(m_ERRClientOldManagers[0]);
PrintFormat("Reintroduced [terrain client]: drain retained mode=%1 incoming=%2", mode, incoming);
ERR_CaptureTerrainSupportStations(m_ERROldSupport);
if (mode) mode.OnGameEnd();
// Owner entities can already be invalid while native destruction is deferred.
// Compare the captured component itself, never clear a newer singleton.
@@ -0,0 +1,315 @@
// Nearby garages are discovered incrementally. Each callback performs one
// survey, maintenance, planning, resource-load or native-spawn step.
class ERR_GarageVehicleRules
{
static bool Garage(string path)
{
path.ToLower();
return path.EndsWith("/garage_e_01.et") || path.EndsWith("/houseaddon_garage_e_01.et");
}
static bool Envelope(vector low, vector high)
{
return ERR_ForceRules.FiniteVector(low) && ERR_ForceRules.FiniteVector(high)
&& low[0] >= -1.05 && high[0] <= 1.05 && low[2] >= -2.4 && high[2] <= 2.4
&& low[1] >= -0.3 && high[1] <= 1.9
&& high[0] > low[0] && high[1] > low[1] && high[2] > low[2];
}
}
class ERR_GarageSite
{
IEntity Garage;
bool Consumed;
bool Selected;
float NextTry;
}
class ERR_ParkedVehicle
{
Vehicle Entity;
ref ERR_GarageSite Site;
vector Origin;
float HiddenSince = -1;
}
class ERR_GarageVehicles
{
protected ERR_GameMode m_Mode;
protected ref array<ref ERR_GarageSite> m_Sites = {};
protected ref array<ref ERR_ParkedVehicle> m_Parked = {};
protected ref map<string, bool> m_KnownCells = new map<string, bool>();
protected ref ERR_MotorbikeCatalog m_Bikes = new ERR_MotorbikeCatalog();
protected ref array<ResourceName> m_LoadedNames = {};
protected ref array<ref Resource> m_LoadedResources = {};
protected ref ERR_GarageSite m_Pending;
protected vector m_Position, m_Angles;
protected ResourceName m_Prefab;
protected bool m_PendingBike;
protected int m_Stage, m_Work, m_SiteCursor, m_MaintainCursor, m_PlayerCursor;
protected int m_Requested, m_Spawned, m_Claimed, m_BikeCandidate;
protected int m_GarageChance, m_SpawnRadius, m_SpawnInterval, m_CleanupRadius;
protected float m_NextSpawn, m_NextStep;
protected bool m_Stopped, m_Started;
int Spawned() { return m_Spawned; }
void ERR_GarageVehicles(ERR_GameMode mode) { m_Mode = mode; }
protected float Now() { return GetGame().GetWorld().GetWorldTime(); }
protected bool Running() { return m_Mode && m_Mode == ERR_GameMode.Get() && m_Mode.Active(); }
void Start()
{
if (!Replication.IsServer() || m_Stopped || m_Started) return;
m_Started = true; m_Requested = m_Mode.Settings().Get("garageVehicleCount");
m_GarageChance = m_Mode.Settings().Get("garageChancePercent");
m_SpawnRadius = m_Mode.Settings().Get("garageRadiusMetres");
m_SpawnInterval = m_Mode.Settings().Get("garageSpawnIntervalSeconds") * 1000;
m_CleanupRadius = Math.Max(1400, m_SpawnRadius + 400);
if (m_Requested <= 0 || m_GarageChance <= 0) { Stop(); return; }
m_Bikes.Find();
GetGame().GetCallqueue().CallLater(Step, 250, true);
}
protected bool Observers(array<vector> positions, array<IEntity> actors)
{
positions.Clear(); actors.Clear();
array<int> ids = {}; GetGame().GetPlayerManager().GetPlayers(ids);
SCR_EditorManagerCore editors = SCR_EditorManagerCore.Cast(SCR_EditorManagerCore.GetInstance(SCR_EditorManagerCore));
foreach (int id : ids)
{
SCR_PlayerController controller = SCR_PlayerController.Cast(GetGame().GetPlayerManager().GetPlayerController(id));
if (!controller) return false;
SCR_EditorManagerEntity editor; if (editors) editor = editors.GetEditorManager(id);
if (editor && editor.IsOpened()) return false;
IEntity body = controller.GetControlledEntity();
if (body)
{
if (!ERR_ForceRules.FiniteVector(body.GetOrigin())) return false;
positions.Insert(body.GetOrigin()); actors.Insert(body);
}
if (controller.ERR_ObserverState() != ERR_ObserverReason.NONE)
{
IEntity watched = controller.ERR_ObservedEntity();
if (!watched || !ERR_ForceRules.FiniteVector(watched.GetOrigin())) return false;
positions.Insert(watched.GetOrigin()); actors.Insert(watched);
}
else if (!body) return false;
}
return true;
}
protected bool Nearby(vector position, array<vector> observers, float radius)
{
foreach (vector observer : observers) if (vector.DistanceXZ(position, observer) <= radius) return true;
return false;
}
// Sample both ends/sides and roof, not just the hidden centre of a garage.
// Open doors and windows can reveal a bay; no distance alone proves it hidden.
protected bool Hidden(vector position, vector angles, array<IEntity> actors, IEntity ignored = null)
{
vector matrix[4]; Math3D.AnglesToMatrix(angles, matrix);
foreach (IEntity actor : actors)
{
if (!actor) return false;
array<IEntity> excluded = {actor};
IEntity ride = CompartmentAccessComponent.GetVehicleIn(actor); if (ride) excluded.Insert(ride);
for (int height = 0; height < 2; height++)
for (int x = -1; x <= 1; x++)
for (int z = -1; z <= 1; z++)
{
TraceParam ray = new TraceParam();
ray.Start = actor.GetOrigin() + "0 1.7 0";
ray.End = position + matrix[0] * (x * 1.1) + matrix[2] * (z * 2.5) + Vector(0, 0.5 + height * 1.3, 0);
ray.Flags = TraceFlags.WORLD | TraceFlags.ENTS; ray.LayerMask = TRACE_LAYER_CAMERA;
ray.ExcludeArray = excluded;
float fraction = GetGame().GetWorld().TraceMove(ray, null);
if (!ERR_ForceRules.Finite(fraction) || fraction >= 0.995 || (ignored && ERR_ConvoyRules.PartOf(ray.TraceEnt, ignored))) return false;
}
}
return true;
}
protected bool Filter(IEntity entity) { return entity && SCR_DestructibleBuildingEntity.Cast(entity); }
protected bool Remember(IEntity entity)
{
if (m_Sites.Count() >= 2048) return false;
if (!entity.GetPrefabData() || !ERR_GarageVehicleRules.Garage(entity.GetPrefabData().GetPrefabName())) return true;
foreach (ERR_GarageSite site : m_Sites) if (site.Garage == entity) return true;
ERR_GarageSite added = new ERR_GarageSite(); added.Garage = entity;
added.Selected = Math.RandomInt(0, 100) < m_GarageChance;
m_Sites.Insert(added);
return true;
}
protected string CellKey(vector point) { return Math.Floor(point[0] / 500).ToString() + ":" + Math.Floor(point[2] / 500).ToString(); }
protected void Discover(array<vector> observers, float now)
{
if (observers.IsEmpty() || m_Sites.Count() >= 2048 || m_KnownCells.Count() >= 16384) return;
// Round-robin observers, nearest unvisited cell first. Each update queries
// at most one 500m cell; teammates share already scanned cells.
if (m_PlayerCursor >= observers.Count()) m_PlayerCursor = 0;
vector observer = observers[m_PlayerCursor++];
int cx = Math.Floor(observer[0] / 500), cz = Math.Floor(observer[2] / 500);
for (int ring = 0; ring <= Math.Ceil(m_SpawnRadius / 500.0); ring++)
for (int x = -ring; x <= ring; x++)
for (int z = -ring; z <= ring; z++)
{
if (Math.Max(Math.AbsInt(x), Math.AbsInt(z)) != ring) continue;
vector low = Vector((cx + x) * 500, 0, (cz + z) * 500);
string key = CellKey(low);
if (m_KnownCells.Contains(key) || !Nearby(low + "250 0 250", observers, m_CleanupRadius)) continue;
m_KnownCells.Set(key, true);
vector boundsLow, boundsHigh; GetGame().GetWorldEntity().GetWorldBounds(boundsLow, boundsHigh);
low[1] = boundsLow[1] - 100; vector high = low + "500 0 500"; high[1] = boundsHigh[1] + 100;
GetGame().GetWorld().QueryEntitiesByAABB(low, high, Remember, Filter, EQueryEntitiesFlags.ALL);
return;
}
}
protected bool InUse(ERR_ParkedVehicle parked)
{
if (!parked.Entity) return false;
// Movement also protects a towed, pushed or otherwise used car, even if a
// compartment callback was missed. Never reclaim somebody's transport.
if (vector.Distance(parked.Entity.GetOrigin(), parked.Origin) > 3) return true;
array<BaseCompartmentSlot> seats = {};
if (!ERR_ConvoyRules.CollectSeats(parked.Entity, seats)) return true;
foreach (BaseCompartmentSlot seat : seats) if (seat.GetOccupant()) return true;
return false;
}
protected void Claim(int index)
{
ERR_ParkedVehicle parked = m_Parked[index];
parked.Site.Consumed = true;
if (parked.Entity) m_Mode.Own(parked.Entity);
m_Claimed++; m_Parked.Remove(index);
}
void PlayerEntered(IEntity actor, IEntity entered)
{
if (m_Stopped || !Replication.IsServer() || !ERR_Actors.Player(actor)) return;
for (int i = m_Parked.Count() - 1; i >= 0; i--)
if (m_Parked[i].Entity && ERR_ConvoyRules.PartOf(entered, m_Parked[i].Entity)) Claim(i);
}
protected void Maintain(array<vector> observers, array<IEntity> actors, float now, bool known)
{
if (m_Parked.IsEmpty()) return;
if (m_MaintainCursor >= m_Parked.Count()) m_MaintainCursor = 0;
int index = m_MaintainCursor++; ERR_ParkedVehicle parked = m_Parked[index];
if (!parked.Entity) { parked.Site.Consumed = true; m_Parked.Remove(index); return; }
if (InUse(parked)) { Claim(index); return; }
if (!known || !ERR_CivilianTrafficRules.Far(parked.Entity.GetOrigin(), observers, m_CleanupRadius)
|| !Hidden(parked.Entity.GetOrigin(), parked.Entity.GetYawPitchRoll(), actors, parked.Entity)) { parked.HiddenSince = -1; return; }
if (parked.HiddenSince < 0) parked.HiddenSince = now;
if (!ERR_CivilianTrafficRules.CleanupDue(now, parked.HiddenSince)) return;
// Recheck occupancy immediately before deletion. Claimed cars are no longer
// in this collection and cannot pass through the retirement path.
if (InUse(parked)) { Claim(index); return; }
SCR_GarbageSystem garbage = SCR_GarbageSystem.GetByEntityWorld(parked.Entity);
if (garbage) garbage.UpdateBlacklist(parked.Entity, false);
ERR_Actors.DeleteSpawned(parked.Entity); m_Parked.Remove(index);
}
protected bool Occupied(ERR_GarageSite site)
{
foreach (ERR_ParkedVehicle parked : m_Parked) if (parked.Site == site) return true;
return false;
}
protected void Plan(array<vector> observers, array<IEntity> actors, float now)
{
if (observers.IsEmpty() || now < m_NextSpawn || m_Parked.Count() >= m_Requested || m_Parked.Count() + m_Claimed >= 128 || m_Sites.IsEmpty()) return;
// Bounded planning work, even after a long tour of a large terrain.
for (int candidate = 0; candidate < 8; candidate++)
{
if (m_SiteCursor >= m_Sites.Count()) m_SiteCursor = 0;
ERR_GarageSite site = m_Sites[m_SiteCursor++];
if (!site.Selected || site.Consumed || !site.Garage || now < site.NextTry || Occupied(site)) continue;
vector position = site.Garage.CoordToParent("0 0 -0.5");
if (!Nearby(position, observers, m_SpawnRadius)) continue;
site.NextTry = now + 15000;
if (!ERR_CivilianTrafficRules.Far(position, observers, 35) || !ERR_Actors.Alive(site.Garage)) continue;
vector transform[4]; site.Garage.GetWorldTransform(transform);
TraceParam floor = new TraceParam(); floor.Start = position + "0 1 0"; floor.End = position - "0 1 0";
floor.Flags = TraceFlags.WORLD | TraceFlags.ENTS;
float fraction = GetGame().GetWorld().TraceMove(floor, null);
if (!ERR_ForceRules.Finite(fraction) || fraction >= 1 || floor.TraceNorm[1] < 0.98) return;
position = vector.Lerp(floor.Start, floor.End, fraction) + "0 0.08 0";
vector angles = site.Garage.GetYawPitchRoll();
if (ChimeraWorldUtils.TryGetWaterSurfaceSimple(GetGame().GetWorld(), position) || !Clear(position, transform) || !Hidden(position, angles, actors)) return;
m_Pending = site; m_Position = position; m_Angles = angles;
m_Prefab = ERR_CivilianTraffic.CarPrefab(Math.RandomInt(0, 2));
m_PendingBike = m_Spawned % 4 == 3 && m_BikeCandidate < m_Bikes.Prefabs.Count();
if (m_PendingBike) m_Prefab = m_Bikes.Prefabs[m_BikeCandidate];
m_Stage = 1; return;
}
}
protected void Pending(array<vector> observers, array<IEntity> actors)
{
if (m_Stage == 1)
{
if (!m_LoadedNames.Contains(m_Prefab))
{
Resource resource = Resource.Load(m_Prefab);
if (!resource || !resource.IsValid()) { if (m_PendingBike) m_BikeCandidate++; m_Pending = null; return; }
m_LoadedNames.Insert(m_Prefab); m_LoadedResources.Insert(resource);
}
m_Stage = 2; return;
}
ERR_GarageSite site = m_Pending; m_Pending = null;
if (!site.Garage || site.Consumed || !ERR_Actors.Alive(site.Garage) || !Nearby(m_Position, observers, m_SpawnRadius)
|| !ERR_CivilianTrafficRules.Far(m_Position, observers, 35) || !Hidden(m_Position, m_Angles, actors)) return;
vector transform[4]; Math3D.AnglesToMatrix(m_Angles, transform);
if (!Clear(m_Position, transform)) return;
m_NextSpawn = Now() + m_SpawnInterval;
IEntity entity = ERR_Actors.Spawn(m_Prefab, m_Position, m_Angles);
Vehicle vehicle = Vehicle.Cast(entity);
vector low, high; if (vehicle) vehicle.GetBounds(low, high);
array<BaseCompartmentSlot> seats = {}; bool driver; bool armed;
if (vehicle && ERR_ConvoyRules.CollectSeats(vehicle, seats))
foreach (BaseCompartmentSlot seat : seats)
{
if (seat.GetType() == ECompartmentType.TURRET) armed = true;
if (seat.GetType() == ECompartmentType.PILOT && seat.IsCompartmentAccessible() && !seat.GetOccupant()) driver = true;
}
if (!driver || armed || !ERR_GarageVehicleRules.Envelope(low, high) || !Clear(m_Position, transform, entity))
{
ERR_Actors.DeleteSpawned(entity); if (m_PendingBike) m_BikeCandidate++; return;
}
Faction civilian = GetGame().GetFactionManager().GetFactionByKey("CIV");
if (civilian) ERR_Actors.SetFaction(vehicle, civilian);
SCR_FuelManagerComponent fuel = SCR_FuelManagerComponent.Cast(vehicle.FindComponent(SCR_FuelManagerComponent));
if (fuel) fuel.SetTotalFuelPercentage(Math.RandomFloat(0.3, 0.8));
ERR_ParkedVehicle parked = new ERR_ParkedVehicle(); parked.Entity = vehicle; parked.Site = site; parked.Origin = m_Position;
m_Parked.Insert(parked); m_Spawned++;
SCR_GarbageSystem garbage = SCR_GarbageSystem.GetByEntityWorld(vehicle);
if (garbage) garbage.UpdateBlacklist(vehicle, true);
}
protected void Step()
{
if (m_Stopped || !Replication.IsServer() || !Running()) return;
float now = Now();
if (now < m_NextStep) return;
m_NextStep = now + 250; // Do not catch up missed work in one slow frame.
array<vector> observers = {}; array<IEntity> actors = {};
bool known = Observers(observers, actors);
if (!known)
{
m_Pending = null;
// Unknown cameras invalidate every retirement grace period immediately.
foreach (ERR_ParkedVehicle parked : m_Parked) parked.HiddenSince = -1;
Maintain(observers, actors, now, false); return;
}
if (m_Pending) { Pending(observers, actors); return; }
m_Work = (m_Work + 1) % 3;
if (m_Work == 0) Maintain(observers, actors, now, true);
else if (m_Work == 1) Discover(observers, now);
else Plan(observers, actors, now);
}
// Filled oriented body volume includes furniture, doors, other vehicles and
// players. The garage itself is deliberately NOT excluded from collision.
protected bool Clear(vector position, vector transform[4], IEntity ignored = null)
{
TraceOBB body = new TraceOBB();
body.Start = position; body.End = position + "0 0.01 0";
body.Mins = "-1.1 0.12 -2.5"; body.Maxs = "1.1 2 2.5";
for (int i = 0; i < 3; i++) body.Mat[i] = transform[i];
body.Flags = TraceFlags.WORLD | TraceFlags.ENTS | TraceFlags.OCEAN;
body.Exclude = ignored;
float fraction = GetGame().GetWorld().TraceMove(body, null);
return ERR_ForceRules.Finite(fraction) && fraction >= 1;
}
void Stop()
{
m_Stopped = true;
if (GetGame()) GetGame().GetCallqueue().Remove(Step);
m_Pending = null; m_Sites.Clear();
// No visible deletion at round end. World teardown owns remaining vehicles.
}
}
@@ -915,6 +915,7 @@ modded class SCR_CompartmentAccessComponent
if (mode) hq = ERR_HQ.Cast(mode.Command());
if (hq && hq.Hunters()) hq.Hunters().PlayerEntered(GetOwner(), targetEntity);
if (mode && mode.CivilianTraffic()) mode.CivilianTraffic().PlayerEntered(GetOwner(), targetEntity);
if (mode && mode.GarageVehicles()) mode.GarageVehicles().PlayerEntered(GetOwner(), targetEntity);
}
super.OnCompartmentEntered(targetEntity, manager, mgrID, slotID, move);
}
@@ -62,7 +62,7 @@ class ERR_RecommendedAddons
static void Snapshot(ERR_GameMode mode, array<string> values)
{
array<string> loaded = {}; GameProject.GetLoadedAddons(loaded);
Entry(values, "bikes", loaded, {"6503091A7B264FC3", "61B84088181A8FA4", "629B2BA37EFFD577"}, {"WCS Motorbikes", "Motorbike", "WCS Armaments"}, mode.Settings().Get("motorbikeCount") > 0);
Entry(values, "bikes", loaded, {"6503091A7B264FC3", "61B84088181A8FA4", "629B2BA37EFFD577"}, {"WCS Motorbikes", "Motorbike", "WCS Armaments"}, mode.Settings().Get("motorbikeCount") > 0 || (mode.Settings().Get("garageVehicleCount") > 0 && mode.Settings().Get("garageChancePercent") > 0));
Entry(values, "plane", loaded, {"69BAEE0D439966FE", "69A8A34027DA65C5"}, {"Cessna 172", "Propeller Flight Core"}, mode.Settings().Get("planeReconEnabled") == 1);
Entry(values, "weapons", loaded, {"BFC890A6F1603EC0"}, {"Phaseline Marksman Rifles"}, mode.Settings().Get("automaticEquipmentChancePercent") != 0);
ERR_HQ hq = ERR_HQ.Cast(mode.Command());
@@ -4,7 +4,7 @@ class ERR_RoadblockRules
{
// Admission never waits longer for a high count. Remaining posts are built
// by the same safe placement path in background, including after prison unlock.
static int PropCount() { return 18; }
static int PropCount() { return 8; }
static int SearchMilliseconds() { return 12000; }
static int SearchAttempts() { return 300; }
static bool Road(float width, float length, float heightDifference)
@@ -61,10 +61,7 @@ class ERR_Roadblocks
protected ref array<ref Resource> m_Loaded = {};
static const ResourceName COVER = "{21D83950DF36F250}Prefabs/Props/Military/Sandbags/Sandbag_01_long_burlap.et";
static const ResourceName BARRIER = "{64C9C1D1C2C1B38D}Prefabs/Props/Infrastructure/Roadblocks/RoadBlock_01.et";
static const ResourceName OBSTACLE = "{A46D45922F8CAD6C}Prefabs/Props/Military/Fortification/CzechHedgehog_01_painted.et";
static const ResourceName SIGN = "{C1977A326F2AA011}Prefabs/Structures/Signs/Military/SignCheckpoint_01_stop.et";
static const ResourceName SHELTER = "{01AE5FD77A9A4C21}Prefabs/Structures/Military/Camps/TentSmallUS_01/TentSmallUS_01.et";
static const ResourceName TABLE = "{53506DCB398C7EBE}Prefabs/Props/Furniture/Table_01.et";
void ERR_Roadblocks(ERR_GameMode mode) { m_Mode = mode; m_Mining = new ERR_RoadblockMining(mode); }
void MineTick(array<ref ERR_HQUnit> units, array<IEntity> attempted, float now) { if (!m_Stopped) m_Mining.Tick(units, attempted, now); }
bool Contains(SCR_AIGroup group) { return group && m_Groups.Contains(group); }
@@ -93,7 +90,7 @@ class ERR_Roadblocks
if (profile && profile.IsValid()) source = profile.GetResource().ToEntitySource();
if (!source || !source.Get("m_aUnitPrefabSlots", m_Roster) || m_Roster.IsEmpty()) return false;
// Preload before physical creation. A failed prefab never leaves half a post.
array<ResourceName> resources = {COVER, BARRIER, OBSTACLE, SIGN, SHELTER, TABLE, "{EC9F1AA42D0B7863}Prefabs/ERR/PrisonPatrol.et", ERR_Resources.DefendWaypoint()};
array<ResourceName> resources = {COVER, BARRIER, SIGN, "{EC9F1AA42D0B7863}Prefabs/ERR/PrisonPatrol.et", ERR_Resources.DefendWaypoint()};
foreach (ResourceName actor : m_Roster) resources.Insert(actor);
foreach (ResourceName prefab : resources)
{
@@ -142,7 +139,7 @@ class ERR_Roadblocks
m_Right = Vector(m_Forward[2], 0, -m_Forward[0]);
// Protected guard positions sit on the shoulders; approach barriers narrow
// wider roads while preserving a straight vehicle passage on every width.
m_Shoulder = Math.Max(road.GetWidth() * 0.5 + 1.5, 4);
m_Shoulder = Math.Max(road.GetWidth() * 0.5 + 0.5, 3.2);
m_Candidates++; m_CandidateStarted = System.GetTickCount();
return true;
}
@@ -190,21 +187,13 @@ class ERR_Roadblocks
// Footprints enclose the native model bounds, including small placement margins.
for (int side = -1; side <= 1; side += 2)
{
// Two front walls, an approach wall and an outer return form
// a sheltered bay for each pair. The rear stays open for guard access.
// A short sandbag screen protects each pair without a wide outer bay.
AddProp(covers, COVER, side * m_Shoulder, -2, m_Right, 1.9, 0.7, 1);
AddProp(covers, COVER, side * m_Shoulder, 2, m_Right, 1.9, 0.7, 1);
AddProp(covers, COVER, side * (m_Shoulder + 2.8), side * 4.8, m_Forward, 1.9, 0.7, 1);
AddProp(covers, COVER, side * (m_Shoulder + 5.6), side * 2, m_Right, 1.9, 0.7, 1);
// Paired stop-line barriers funnel traffic into a >=4m centre passage.
AddProp(covers, BARRIER, side * 3.3, side * 8, m_Forward, 1.2, 0.4, 1.3);
AddProp(covers, BARRIER, side * 5.9, side * 8, m_Forward, 1.2, 0.4, 1.3);
AddProp(covers, OBSTACLE, side * (m_Shoulder + 4.5), side * 8, m_Forward, 1, 1, 1.6);
AddProp(covers, SIGN, side * 3, side * 12, m_Forward * (side * 1.0), 0.45, 0.4, 0.8);
// One barrier and sign per approach preserve a >=4m centre passage.
AddProp(covers, BARRIER, side * 3.3, side * 6, m_Forward, 1.2, 0.4, 1.3);
AddProp(covers, SIGN, side * 3, side * 8.5, m_Forward * (side * 1.0), 0.45, 0.4, 0.8);
}
// A small inspection/rest area behind one bay, away from the vehicle lane.
AddProp(covers, TABLE, m_Shoulder + 3, -4.8, m_Forward, 0.9, 0.7, 1.1);
AddProp(covers, SHELTER, m_Shoulder + 3, -8.5, m_Forward, 1.2, 2.4, 1.4);
}
protected void AddProp(array<ref ERR_RoadblockProp> covers, ResourceName prefab, float across, float along, vector forward, float halfWidth, float halfDepth, float height)
@@ -236,7 +225,7 @@ class ERR_Roadblocks
m_BuildPositions.Clear(); m_BuildCovers.Clear();
}
// A free Game Master camera does not force-stream bare, non-editable props
// with the nearby editable guards. Keep this bounded bank (<=540 props) on
// with the nearby editable guards. Keep this bounded bank (<=240 props) on
// clients, including late joiners, rather than tying it to the player's body.
// Do not report a completed post if any physical prop lacks a network node.
protected bool ShareProps()
@@ -306,6 +295,10 @@ class ERR_Roadblocks
m_BuildPositions.Clear(); m_BuildCovers.Clear();
m_NextSearch = System.GetTickCount() + 2000;
}
protected vector GuardPosition(int side, int row)
{
return ERR_Actors.Ground(m_Centre + m_Right * (side * (m_Shoulder + 1.5)) + m_Forward * (side * (row - 1.5)));
}
// 0: reject, 1: awaiting native tile, 2: ready.
protected int Plan(out array<vector> positions, out array<ref ERR_RoadblockProp> covers)
{
@@ -320,7 +313,7 @@ class ERR_Roadblocks
{
for (int row = -1; row <= 1; row += 2)
{
vector point = ERR_Actors.Ground(m_Centre + m_Right * (side * (m_Shoulder + 2.5)) + m_Forward * (side * (row - 1.5)));
vector point = GuardPosition(side, row);
if (!mesh.IsTileLoaded(point)) { mesh.LoadTileIn(point); return 1; }
vector reachable;
if (!mesh.GetReachablePoint(point, 1, reachable) || vector.DistanceXZ(point, reachable) > 1 || !m_Placement.Clear(point, reserved)) return 0;
+36 -11
View File
@@ -14,12 +14,14 @@ ImageWidgetClass { "Is Visible" 1 Name "Selected_Main_Mission" Slot FrameWidgetS
FrameWidgetClass { "Is Visible" 1 Name "Sidebar_Mission" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 166 OffsetTop 166 SizeX 180 OffsetRight -208 SizeY 480 OffsetBottom -646 } {
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenSettings" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 0 OffsetTop 0 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -42 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "General" } } }
ImageWidgetClass { "Is Visible" 1 Name "Selected_OpenSettings" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 44 OffsetTop 44 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -47 } Color 0.9 0.66 0.25 1 }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenWorld" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 54 OffsetTop 54 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -96 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "World" } } }
ImageWidgetClass { "Is Visible" 0 Name "Selected_OpenWorld" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 98 OffsetTop 98 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -101 } Color 0.9 0.66 0.25 1 }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenTerrains" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 108 OffsetTop 108 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -150 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Terrains" } } }
ImageWidgetClass { "Is Visible" 0 Name "Selected_OpenTerrains" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 152 OffsetTop 152 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -155 } Color 0.9 0.66 0.25 1 }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenTelemetry" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 162 OffsetTop 162 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -204 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Statistics" } } }
ImageWidgetClass { "Is Visible" 0 Name "Selected_OpenTelemetry" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 206 OffsetTop 206 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -209 } Color 0.9 0.66 0.25 1 }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenVehicles" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 54 OffsetTop 54 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -96 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Civilian vehicles" } } }
ImageWidgetClass { "Is Visible" 0 Name "Selected_OpenVehicles" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 98 OffsetTop 98 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -101 } Color 0.9 0.66 0.25 1 }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenWorld" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 108 OffsetTop 108 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -150 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "World" } } }
ImageWidgetClass { "Is Visible" 0 Name "Selected_OpenWorld" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 152 OffsetTop 152 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -155 } Color 0.9 0.66 0.25 1 }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenTerrains" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 162 OffsetTop 162 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -204 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Terrains" } } }
ImageWidgetClass { "Is Visible" 0 Name "Selected_OpenTerrains" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 206 OffsetTop 206 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -209 } Color 0.9 0.66 0.25 1 }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenTelemetry" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 216 OffsetTop 216 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -258 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Statistics" } } }
ImageWidgetClass { "Is Visible" 0 Name "Selected_OpenTelemetry" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 260 OffsetTop 260 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -263 } Color 0.9 0.66 0.25 1 }
} }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "Main_Forces" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 174 OffsetLeft 174 PositionY 106 OffsetTop 106 SizeX 138 OffsetRight -312 SizeY 42 OffsetBottom -148 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Forces" } } }
ImageWidgetClass { "Is Visible" 0 Name "Selected_Main_Forces" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 174 OffsetLeft 174 PositionY 150 OffsetTop 150 SizeX 138 OffsetRight -312 SizeY 3 OffsetBottom -153 } Color 0.9 0.66 0.25 1 }
@@ -121,11 +123,6 @@ TextWidgetClass { Name "Saved_keepWeaponOnUnconscious" Slot FrameWidgetSlot { An
TextWidgetClass { Name "SelfEpiLabel" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 332 OffsetTop 332 SizeX 240 OffsetRight -268 SizeY 28 OffsetBottom -360 } Color 0.9 0.92 0.94 1 Text "Self epinephrine (Space)" "Font Size" 16 "Min Font Size" 16 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "SelfEpiOn" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 280 OffsetLeft 280 PositionY 332 OffsetTop 332 SizeX 90 OffsetRight -370 SizeY 28 OffsetBottom -360 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Enabled" } } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "SelfEpiOff" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 382 OffsetLeft 382 PositionY 332 OffsetTop 332 SizeX 90 OffsetRight -472 SizeY 28 OffsetBottom -360 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Disabled" } } }
TextWidgetClass { Name "Label_civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 244 OffsetTop 244 SizeX 456 OffsetRight -964 SizeY 22 OffsetBottom -266 } Color 0.9 0.92 0.94 1 Text "Civilian traffic (0-6 cars; 0 off)" "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
ButtonWidgetClass : "{DA43C77A0D1E59F8}UI/layouts/ERR/AdminValue.layout" { Name "civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 268 OffsetTop 268 SizeX 180 OffsetRight -688 SizeY 32 OffsetBottom -300 } components { SCR_EditBoxComponent "{547290FFBD5B33E9}" { m_sLabel "" } } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "Apply_civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 700 OffsetLeft 700 PositionY 268 OffsetTop 268 SizeX 100 OffsetRight -800 SizeY 32 OffsetBottom -300 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Apply" } } }
TextWidgetClass { Name "Saved_civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 304 OffsetTop 304 SizeX 456 OffsetRight -964 SizeY 28 OffsetBottom -332 } Color 0.9 0.92 0.94 1 Text "Waiting for server" "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Help_civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 332 OffsetTop 332 SizeX 456 OffsetRight -964 SizeY 28 OffsetBottom -360 } Color 0.9 0.92 0.94 1 Text "0 disables new traffic. Player-used cars are kept." "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Label_restartOnEmpty" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 364 OffsetTop 364 SizeX 456 OffsetRight -484 SizeY 22 OffsetBottom -386 } Color 0.9 0.92 0.94 1 Text "Restart after last disconnect" "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "RestartEmptyOn" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 388 OffsetTop 388 SizeX 140 OffsetRight -168 SizeY 32 OffsetBottom -420 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Enabled" } } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "RestartEmptyOff" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 180 OffsetLeft 180 PositionY 388 OffsetTop 388 SizeX 152 OffsetRight -332 SizeY 32 OffsetBottom -420 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Disabled" } } }
@@ -137,6 +134,34 @@ ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_But
TextWidgetClass { Name "Saved_emptyRestartHours" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 424 OffsetTop 424 SizeX 456 OffsetRight -964 SizeY 24 OffsetBottom -448 } Color 0.9 0.92 0.94 1 Text "Waiting for server" "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Help_emptyRestartHours" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 452 OffsetTop 452 SizeX 456 OffsetRight -964 SizeY 28 OffsetBottom -480 } Color 0.9 0.92 0.94 1 Text "0 off; 1-168 real hours empty. Default 5. Applies now." "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
} }
FrameWidgetClass { "Is Visible" 0 Name "VehiclesPage" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 220 OffsetLeft 220 PositionY 160 OffsetTop 160 SizeX 1000 OffsetRight -1220 SizeY 486 OffsetBottom -646 } {
TextWidgetClass { Name "Label_civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 4 OffsetTop 4 SizeX 456 OffsetRight -484 SizeY 22 OffsetBottom -26 } Color 0.9 0.92 0.94 1 Text "Civilian traffic (0-6 cars; 0 off)" "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
ButtonWidgetClass : "{DA43C77A0D1E59F8}UI/layouts/ERR/AdminValue.layout" { Name "civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 28 OffsetTop 28 SizeX 180 OffsetRight -208 SizeY 32 OffsetBottom -60 } components { SCR_EditBoxComponent "{547290FFBD5B33E9}" { m_sLabel "" } } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "Apply_civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 220 OffsetLeft 220 PositionY 28 OffsetTop 28 SizeX 100 OffsetRight -320 SizeY 32 OffsetBottom -60 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Apply" } } }
TextWidgetClass { Name "Saved_civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 64 OffsetTop 64 SizeX 456 OffsetRight -484 SizeY 28 OffsetBottom -92 } Color 0.9 0.92 0.94 1 Text "Waiting for server" "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Help_civilianTrafficCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 92 OffsetTop 92 SizeX 456 OffsetRight -484 SizeY 28 OffsetBottom -120 } Color 0.9 0.92 0.94 1 Text "0 disables new traffic. Player-used cars are kept." "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Label_garageVehicleCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 4 OffsetTop 4 SizeX 456 OffsetRight -964 SizeY 22 OffsetBottom -26 } Color 0.9 0.92 0.94 1 Text "Nearby garage vehicles (0-24; 0 off)" "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
ButtonWidgetClass : "{DA43C77A0D1E59F8}UI/layouts/ERR/AdminValue.layout" { Name "garageVehicleCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 28 OffsetTop 28 SizeX 180 OffsetRight -688 SizeY 32 OffsetBottom -60 } components { SCR_EditBoxComponent "{547290FFBD5B33E9}" { m_sLabel "" } } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "Apply_garageVehicleCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 700 OffsetLeft 700 PositionY 28 OffsetTop 28 SizeX 100 OffsetRight -800 SizeY 32 OffsetBottom -60 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Apply" } } }
TextWidgetClass { Name "Saved_garageVehicleCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 64 OffsetTop 64 SizeX 456 OffsetRight -964 SizeY 28 OffsetBottom -92 } Color 0.9 0.92 0.94 1 Text "Waiting for server" "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Help_garageVehicleCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 92 OffsetTop 92 SizeX 456 OffsetRight -964 SizeY 28 OffsetBottom -120 } Color 0.9 0.92 0.94 1 Text "Default 16 unclaimed vehicles. Set 0 to disable; next round." "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Label_garageChancePercent" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 144 OffsetTop 144 SizeX 456 OffsetRight -484 SizeY 22 OffsetBottom -166 } Color 0.9 0.92 0.94 1 Text "Garage selection chance (0-100%)" "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
ButtonWidgetClass : "{DA43C77A0D1E59F8}UI/layouts/ERR/AdminValue.layout" { Name "garageChancePercent" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 168 OffsetTop 168 SizeX 180 OffsetRight -208 SizeY 32 OffsetBottom -200 } components { SCR_EditBoxComponent "{547290FFBD5B33E9}" { m_sLabel "" } } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "Apply_garageChancePercent" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 220 OffsetLeft 220 PositionY 168 OffsetTop 168 SizeX 100 OffsetRight -320 SizeY 32 OffsetBottom -200 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Apply" } } }
TextWidgetClass { Name "Saved_garageChancePercent" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 204 OffsetTop 204 SizeX 456 OffsetRight -484 SizeY 28 OffsetBottom -232 } Color 0.9 0.92 0.94 1 Text "Waiting for server" "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Help_garageChancePercent" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 232 OffsetTop 232 SizeX 456 OffsetRight -484 SizeY 28 OffsetBottom -260 } Color 0.9 0.92 0.94 1 Text "Default 70%; 100 selects every suitable garage. Next round." "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Label_garageRadiusMetres" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 144 OffsetTop 144 SizeX 456 OffsetRight -964 SizeY 22 OffsetBottom -166 } Color 0.9 0.92 0.94 1 Text "Spawn radius (250-1500 metres)" "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
ButtonWidgetClass : "{DA43C77A0D1E59F8}UI/layouts/ERR/AdminValue.layout" { Name "garageRadiusMetres" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 168 OffsetTop 168 SizeX 180 OffsetRight -688 SizeY 32 OffsetBottom -200 } components { SCR_EditBoxComponent "{547290FFBD5B33E9}" { m_sLabel "" } } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "Apply_garageRadiusMetres" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 700 OffsetLeft 700 PositionY 168 OffsetTop 168 SizeX 100 OffsetRight -800 SizeY 32 OffsetBottom -200 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Apply" } } }
TextWidgetClass { Name "Saved_garageRadiusMetres" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 204 OffsetTop 204 SizeX 456 OffsetRight -964 SizeY 28 OffsetBottom -232 } Color 0.9 0.92 0.94 1 Text "Waiting for server" "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Help_garageRadiusMetres" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 232 OffsetTop 232 SizeX 456 OffsetRight -964 SizeY 28 OffsetBottom -260 } Color 0.9 0.92 0.94 1 Text "Distance around players; default 1000 m. Next round." "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Label_garageSpawnIntervalSeconds" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 284 OffsetTop 284 SizeX 456 OffsetRight -484 SizeY 22 OffsetBottom -306 } Color 0.9 0.92 0.94 1 Text "Time between spawns (5-60 seconds)" "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
ButtonWidgetClass : "{DA43C77A0D1E59F8}UI/layouts/ERR/AdminValue.layout" { Name "garageSpawnIntervalSeconds" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 308 OffsetTop 308 SizeX 180 OffsetRight -208 SizeY 32 OffsetBottom -340 } components { SCR_EditBoxComponent "{547290FFBD5B33E9}" { m_sLabel "" } } }
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "Apply_garageSpawnIntervalSeconds" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 220 OffsetLeft 220 PositionY 308 OffsetTop 308 SizeX 100 OffsetRight -320 SizeY 32 OffsetBottom -340 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Apply" } } }
TextWidgetClass { Name "Saved_garageSpawnIntervalSeconds" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 344 OffsetTop 344 SizeX 456 OffsetRight -484 SizeY 28 OffsetBottom -372 } Color 0.9 0.92 0.94 1 Text "Waiting for server" "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "Help_garageSpawnIntervalSeconds" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 372 OffsetTop 372 SizeX 456 OffsetRight -484 SizeY 28 OffsetBottom -400 } Color 0.9 0.92 0.94 1 Text "Default 5 s. Increase to spread out spawning. Next round." "Font Size" 14 "Min Font Size" 14 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "GarageSettingsNote" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 508 OffsetLeft 508 PositionY 284 OffsetTop 284 SizeX 456 OffsetRight -964 SizeY 110 OffsetBottom -394 } Color 0.9 0.92 0.94 1 Text "Garage settings apply next round. Used vehicles stay for the round. Empty garages are filled gradually when hidden from players." "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
} }
FrameWidgetClass { "Is Visible" 0 Name "TelemetryPage" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 220 OffsetLeft 220 PositionY 160 OffsetTop 160 SizeX 1000 OffsetRight -1220 SizeY 486 OffsetBottom -646 } {
TextWidgetClass { Name "TelemetryHeading" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 10 OffsetTop 10 SizeX 936 OffsetRight -964 SizeY 32 OffsetBottom -42 } Color 0.9 0.92 0.94 1 Text "Mission statistics" "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
TextWidgetClass { Name "TelemetryDescription" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 55 OffsetTop 55 SizeX 936 OffsetRight -964 SizeY 80 OffsetBottom -135 } Color 0.9 0.92 0.94 1 Text "Share mission starts, progress, results, terrain, loaded mods and player counts with the configured statistics service. Enabled by default." "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }