Refactor station modules into typed runtime models

This commit is contained in:
2026-03-27 14:59:15 -04:00
parent f961ac62b6
commit e8fb033a01
13 changed files with 408 additions and 169 deletions

View File

@@ -162,21 +162,21 @@ internal sealed class InfrastructureSimulationService
private static IEnumerable<string> GetStoragePressureCandidates(SimulationWorld world, StationRuntime station)
{
foreach (var (storageClass, moduleId) in new[]
foreach (var (storageKind, moduleId) in new[]
{
("solid", "module_arg_stor_solid_m_01"),
("liquid", "module_arg_stor_liquid_m_01"),
("container", "module_arg_stor_container_m_01"),
(StorageKind.Solid, "module_arg_stor_solid_m_01"),
(StorageKind.Liquid, "module_arg_stor_liquid_m_01"),
(StorageKind.Container, "module_arg_stor_container_m_01"),
})
{
var capacity = GetStationStorageCapacity(station, storageClass);
var capacity = GetStationStorageCapacity(world, station, storageKind);
if (capacity <= 0.01f)
{
continue;
}
var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoKind == storageClass)
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoStorageKind == storageKind)
.Sum(entry => entry.Value);
if (used / capacity >= 0.65f)
{
@@ -195,14 +195,10 @@ internal sealed class InfrastructureSimulationService
continue;
}
if (GetStorageRequirement(itemDefinition.CargoKind) is { } storageModuleId)
if (GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoStorageKind) is { } storageModuleId)
{
yield return storageModuleId;
}
else
{
yield return "module_arg_stor_container_m_01";
}
}
if (world.ModuleDefinitions.TryGetValue(recipe.ModuleId, out var moduleDefinition))
@@ -214,14 +210,10 @@ internal sealed class InfrastructureSimulationService
continue;
}
if (GetStorageRequirement(itemDefinition.CargoKind) is { } storageModuleId)
if (GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoStorageKind) is { } storageModuleId)
{
yield return storageModuleId;
}
else
{
yield return "module_arg_stor_container_m_01";
}
}
}
}
@@ -324,16 +316,16 @@ internal sealed class InfrastructureSimulationService
string? objectiveCommodityId,
bool requiredByObjective)
{
var storageClass = storageModuleId switch
var storageKind = storageModuleId switch
{
"module_arg_stor_solid_m_01" => "solid",
"module_arg_stor_liquid_m_01" => "liquid",
_ => "container",
"module_arg_stor_solid_m_01" => StorageKind.Solid,
"module_arg_stor_liquid_m_01" => StorageKind.Liquid,
_ => StorageKind.Container,
};
var capacity = GetStationStorageCapacity(station, storageClass);
var capacity = GetStationStorageCapacity(world, station, storageKind);
var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoKind == storageClass)
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoStorageKind == storageKind)
.Sum(entry => entry.Value);
var utilization = capacity <= 0.01f ? 0f : used / capacity;
@@ -342,8 +334,8 @@ internal sealed class InfrastructureSimulationService
if (!string.IsNullOrWhiteSpace(objectiveModuleId) && !string.IsNullOrWhiteSpace(objectiveCommodityId))
{
var objectiveUsesStorage = ModuleNeedsStorageClass(world, objectiveModuleId, storageClass)
|| CommodityUsesStorageClass(world, objectiveCommodityId, storageClass);
var objectiveUsesStorage = ModuleNeedsStorageClass(world, objectiveModuleId, storageKind)
|| CommodityUsesStorageClass(world, objectiveCommodityId, storageKind);
if (objectiveUsesStorage)
{
score += 35f;
@@ -579,15 +571,15 @@ internal sealed class InfrastructureSimulationService
case "module_arg_stor_container_m_01":
case "module_arg_stor_solid_m_01":
case "module_arg_stor_liquid_m_01":
var storageClass = supportModuleId switch
var storageKind = supportModuleId switch
{
"module_arg_stor_solid_m_01" => "solid",
"module_arg_stor_liquid_m_01" => "liquid",
_ => "container",
"module_arg_stor_solid_m_01" => StorageKind.Solid,
"module_arg_stor_liquid_m_01" => StorageKind.Liquid,
_ => StorageKind.Container,
};
if (analysis.HasMissingOutputStorage
&& (ModuleNeedsStorageClass(world, objectiveModuleId, storageClass)
|| CommodityUsesStorageClass(world, objectiveCommodityId, storageClass)))
&& (ModuleNeedsStorageClass(world, objectiveModuleId, storageKind)
|| CommodityUsesStorageClass(world, objectiveCommodityId, storageKind)))
{
unlockScore += 70f;
}
@@ -688,7 +680,7 @@ internal sealed class InfrastructureSimulationService
return demand;
}
private static bool ModuleNeedsStorageClass(SimulationWorld world, string moduleId, string storageClass)
private static bool ModuleNeedsStorageClass(SimulationWorld world, string moduleId, StorageKind storageKind)
{
if (!world.ModuleRecipes.TryGetValue(moduleId, out var recipe))
{
@@ -697,12 +689,12 @@ internal sealed class InfrastructureSimulationService
return recipe.Inputs.Any(input =>
world.ItemDefinitions.TryGetValue(input.ItemId, out var itemDefinition)
&& string.Equals(itemDefinition.CargoKind, storageClass, StringComparison.Ordinal));
&& itemDefinition.CargoStorageKind == storageKind);
}
private static bool CommodityUsesStorageClass(SimulationWorld world, string commodityId, string storageClass) =>
private static bool CommodityUsesStorageClass(SimulationWorld world, string commodityId, StorageKind storageKind) =>
world.ItemDefinitions.TryGetValue(commodityId, out var itemDefinition)
&& string.Equals(itemDefinition.CargoKind, storageClass, StringComparison.Ordinal);
&& itemDefinition.CargoStorageKind == storageKind;
private static bool CanStationAcceptStationOutputSoon(SimulationWorld world, StationRuntime station, string itemId, float amount)
{
@@ -711,14 +703,19 @@ internal sealed class InfrastructureSimulationService
return false;
}
var capacity = GetStationStorageCapacity(station, itemDefinition.CargoKind);
if (itemDefinition.CargoStorageKind is not { } storageKind)
{
return false;
}
var capacity = GetStationStorageCapacity(world, station, storageKind);
if (capacity <= 0.01f)
{
return false;
}
var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && string.Equals(definition.CargoKind, itemDefinition.CargoKind, StringComparison.Ordinal))
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoStorageKind == storageKind)
.Sum(entry => entry.Value);
return used + amount <= capacity * 0.95f;
}

View File

@@ -39,7 +39,7 @@ internal sealed class StationLifecycleService
var requiredWater = station.Population * WaterConsumptionPerWorkerPerSecond * deltaSeconds;
var consumedWater = RemoveInventory(station.Inventory, "water", requiredWater);
var waterSatisfied = requiredWater <= 0.01f || consumedWater + 0.001f >= requiredWater;
var habitatModules = CountModules(station.InstalledModules, world.ModuleDefinitions, ModuleType.Habitation);
var habitatModules = CountStationModules(station, ModuleType.Habitation);
station.PopulationCapacity = 40f + (habitatModules * 220f);
if (waterSatisfied)

View File

@@ -1,5 +1,6 @@
using static SpaceGame.Api.Factions.AI.CommanderPlanningService;
using static SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport;
using SpaceGame.Api.Shared.Runtime;
namespace SpaceGame.Api.Stations.Simulation;
@@ -54,14 +55,14 @@ internal sealed class StationSimulationService
_ => 0f,
};
var oreReserve = role == "refinery" ? 260f : 0f;
var hullpartsReserve = MathF.Max(constructionHullpartsReserve, HasStationModules(station, "module_gen_build_l_01") ? 120f : 0f);
var claytronicsReserve = MathF.Max(constructionClayReserve, HasStationModules(station, "module_gen_build_l_01") ? 120f : 0f);
var hullpartsReserve = MathF.Max(constructionHullpartsReserve, HasShipyardCapability(station) ? 120f : 0f);
var claytronicsReserve = MathF.Max(constructionClayReserve, HasShipyardCapability(station) ? 120f : 0f);
var grapheneReserve = role == "graphene" ? 120f : 0f;
var siliconWafersReserve = role == "siliconwafers" ? 120f : 0f;
var antimatterCellsReserve = role == "antimattercells" ? 120f : 0f;
var superfluidCoolantReserve = role == "superfluidcoolant" ? 120f : 0f;
var quantumTubesReserve = role == "quantumtubes" ? 120f : 0f;
var shipPartsReserve = HasStationModules(station, "module_gen_build_l_01")
var shipPartsReserve = HasShipyardCapability(station)
&& GetShipProductionPressure(world, station.FactionId, "military") > 0.2f
? 90f
: 0f;
@@ -116,7 +117,7 @@ internal sealed class StationSimulationService
var constructionHullpartsReserve = GetConstructionDemandForItem(world, site, "hullparts");
var constructionClayReserve = GetConstructionDemandForItem(world, site, "claytronics");
var constructionRefinedReserve = GetConstructionDemandForItem(world, site, "refinedmetals");
var shipPartsReserve = HasStationModules(station, "module_gen_build_l_01")
var shipPartsReserve = HasShipyardCapability(station)
&& GetShipProductionPressure(world, station.FactionId, "military") > 0.2f
? 90f
: 0f;
@@ -151,8 +152,8 @@ internal sealed class StationSimulationService
"refinery" => 80f,
_ => 0f,
}, constructionRefinedReserve),
"hullparts" => MathF.Max(constructionHullpartsReserve, HasStationModules(station, "module_gen_build_l_01") ? 120f : 0f) + shipPartsReserve,
"claytronics" => MathF.Max(constructionClayReserve, HasStationModules(station, "module_gen_build_l_01") ? 120f : 0f),
"hullparts" => MathF.Max(constructionHullpartsReserve, HasShipyardCapability(station) ? 120f : 0f) + shipPartsReserve,
"claytronics" => MathF.Max(constructionClayReserve, HasShipyardCapability(station) ? 120f : 0f),
"graphene" => MathF.Max(role == "graphene" ? 120f : 0f, role == "quantumtubes" ? 160f : 0f),
"siliconwafers" => role == "siliconwafers" ? 120f : 0f,
"antimattercells" => MathF.Max(role == "antimattercells" ? 120f : 0f, role == "claytronics" ? 120f : 0f),
@@ -292,7 +293,7 @@ internal sealed class StationSimulationService
if (outputItemIds.Contains("hullparts"))
{
return HasStationModules(station, "module_gen_prod_advancedelectronics_01", "module_gen_build_l_01")
return HasShipyardCapability(station) && HasStationModules(station, "module_gen_prod_advancedelectronics_01")
? -140f * MathF.Max(expansionPressure, fleetPressure)
: 280f * MathF.Max(expansionPressure, fleetPressure);
}
@@ -407,20 +408,25 @@ internal sealed class StationSimulationService
return false;
}
var requiredModule = GetStorageRequirement(itemDefinition.CargoKind);
if (requiredModule is not null && !station.InstalledModules.Contains(requiredModule, StringComparer.Ordinal))
var storageKind = itemDefinition.CargoStorageKind;
if (storageKind is null)
{
return false;
}
var capacity = GetStationStorageCapacity(station, itemDefinition.CargoKind);
if (!HasStorageCapacity(world, station, storageKind.Value))
{
return false;
}
var capacity = GetStationStorageCapacity(world, station, storageKind.Value);
if (capacity <= 0.01f)
{
return false;
}
var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoKind == itemDefinition.CargoKind)
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoStorageKind == storageKind)
.Sum(entry => entry.Value);
return used + amount <= capacity + 0.001f;
}
@@ -455,7 +461,7 @@ internal sealed class StationSimulationService
return objective;
}
if (HasStationModules(station, "module_gen_build_l_01"))
if (HasShipyardCapability(station))
{
return "shipyard";
}
@@ -513,6 +519,9 @@ internal sealed class StationSimulationService
return "general";
}
private static bool HasShipyardCapability(StationRuntime station) =>
CountStationModules(station, ModuleType.BuildModule) > 0;
private static float GetConstructionDemandForItem(SimulationWorld world, ConstructionSiteRuntime? site, string itemId)
{
if (site is null || !site.RequiredItems.TryGetValue(itemId, out var required))