feat: simplify station production

This commit is contained in:
2026-03-17 20:38:13 -04:00
parent 0ae53b76e8
commit b3508d9d08
4 changed files with 29 additions and 63 deletions

View File

@@ -98,6 +98,7 @@ public sealed class ModuleDefinition
public string Description { get; set; } = string.Empty; public string Description { get; set; } = string.Empty;
public required string Type { get; set; } public required string Type { get; set; }
public string? Product { get; set; } public string? Product { get; set; }
public string ProductionMode { get; set; } = "passive";
public float Radius { get; set; } = 12f; public float Radius { get; set; } = 12f;
public float Hull { get; set; } = 100f; public float Hull { get; set; } = 100f;
public float WorkforceNeeded { get; set; } public float WorkforceNeeded { get; set; }

View File

@@ -565,7 +565,7 @@ public sealed partial class SimulationEngine
station.MarketOrderIds.OrderBy(orderId => orderId, StringComparer.Ordinal).ToList()); station.MarketOrderIds.OrderBy(orderId => orderId, StringComparer.Ordinal).ToList());
private static IReadOnlyList<StationActionProgressSnapshot> ToStationActionProgressSnapshots(SimulationWorld world, StationRuntime station) => private static IReadOnlyList<StationActionProgressSnapshot> ToStationActionProgressSnapshots(SimulationWorld world, StationRuntime station) =>
GetStationProductionLanes(station) GetStationProductionLanes(world, station)
.Select(laneKey => .Select(laneKey =>
{ {
var recipe = SelectProductionRecipe(world, station, laneKey); var recipe = SelectProductionRecipe(world, station, laneKey);

View File

@@ -86,7 +86,7 @@ public sealed partial class SimulationEngine
private void RunStationProduction(SimulationWorld world, StationRuntime station, float deltaSeconds, ICollection<SimulationEventRecord> events) private void RunStationProduction(SimulationWorld world, StationRuntime station, float deltaSeconds, ICollection<SimulationEventRecord> events)
{ {
var faction = world.Factions.FirstOrDefault(candidate => candidate.Id == station.FactionId); var faction = world.Factions.FirstOrDefault(candidate => candidate.Id == station.FactionId);
foreach (var laneKey in GetStationProductionLanes(station)) foreach (var laneKey in GetStationProductionLanes(world, station))
{ {
var recipe = SelectProductionRecipe(world, station, laneKey); var recipe = SelectProductionRecipe(world, station, laneKey);
if (recipe is null) if (recipe is null)
@@ -95,7 +95,7 @@ public sealed partial class SimulationEngine
continue; continue;
} }
var throughput = GetStationProductionThroughput(station, recipe); var throughput = GetStationProductionThroughput(world, station, recipe);
var produced = 0f; var produced = 0f;
station.ProductionLaneTimers[laneKey] = GetStationProductionTimer(station, laneKey) + (deltaSeconds * station.WorkforceEffectiveRatio * throughput); station.ProductionLaneTimers[laneKey] = GetStationProductionTimer(station, laneKey) + (deltaSeconds * station.WorkforceEffectiveRatio * throughput);
@@ -132,26 +132,21 @@ public sealed partial class SimulationEngine
} }
} }
private static IEnumerable<string> GetStationProductionLanes(StationRuntime station) private static IEnumerable<string> GetStationProductionLanes(SimulationWorld world, StationRuntime station)
{ {
if (CountModules(station.InstalledModules, "refinery-stack") > 0) foreach (var moduleId in station.InstalledModules.Distinct(StringComparer.Ordinal))
{ {
yield return "refinery"; if (!world.ModuleDefinitions.TryGetValue(moduleId, out var def) || string.IsNullOrEmpty(def.ProductionMode))
} {
continue;
}
if (CountModules(station.InstalledModules, "fabricator-array") > 0) if (string.Equals(def.ProductionMode, "commanded", StringComparison.Ordinal) && station.CommanderId is null)
{ {
yield return "fabrication"; continue;
} }
if (CountModules(station.InstalledModules, "component-factory") > 0) yield return moduleId;
{
yield return "components";
}
if (CountModules(station.InstalledModules, "ship-factory") > 0)
{
yield return "shipyard";
} }
} }
@@ -160,34 +155,13 @@ public sealed partial class SimulationEngine
private static RecipeDefinition? SelectProductionRecipe(SimulationWorld world, StationRuntime station, string laneKey) => private static RecipeDefinition? SelectProductionRecipe(SimulationWorld world, StationRuntime station, string laneKey) =>
world.Recipes.Values world.Recipes.Values
.Where(recipe => RecipeAppliesToStation(station, recipe) && string.Equals(GetStationProductionLaneKey(recipe), laneKey, StringComparison.Ordinal)) .Where(recipe => RecipeAppliesToStation(station, recipe) && string.Equals(GetStationProductionLaneKey(world, recipe), laneKey, StringComparison.Ordinal))
.OrderByDescending(recipe => GetStationRecipePriority(world, station, recipe)) .OrderByDescending(recipe => GetStationRecipePriority(world, station, recipe))
.FirstOrDefault(recipe => CanRunRecipe(world, station, recipe)); .FirstOrDefault(recipe => CanRunRecipe(world, station, recipe));
private static string? GetStationProductionLaneKey(RecipeDefinition recipe) private static string? GetStationProductionLaneKey(SimulationWorld world, RecipeDefinition recipe) =>
{ recipe.RequiredModules.FirstOrDefault(moduleId =>
if (recipe.RequiredModules.Contains("refinery-stack", StringComparer.Ordinal)) world.ModuleDefinitions.TryGetValue(moduleId, out var def) && !string.IsNullOrEmpty(def.ProductionMode));
{
return "refinery";
}
if (recipe.RequiredModules.Contains("fabricator-array", StringComparer.Ordinal))
{
return "fabrication";
}
if (recipe.RequiredModules.Contains("component-factory", StringComparer.Ordinal))
{
return "components";
}
if (recipe.RequiredModules.Contains("ship-factory", StringComparer.Ordinal))
{
return "shipyard";
}
return null;
}
private static float GetStationRecipePriority(SimulationWorld world, StationRuntime station, RecipeDefinition recipe) private static float GetStationRecipePriority(SimulationWorld world, StationRuntime station, RecipeDefinition recipe)
{ {
@@ -467,29 +441,15 @@ public sealed partial class SimulationEngine
}; };
} }
private static float GetStationProductionThroughput(StationRuntime station, RecipeDefinition recipe) private static float GetStationProductionThroughput(SimulationWorld world, StationRuntime station, RecipeDefinition recipe)
{ {
if (recipe.RequiredModules.Contains("refinery-stack", StringComparer.Ordinal)) var laneModuleId = GetStationProductionLaneKey(world, recipe);
if (laneModuleId is null)
{ {
return Math.Max(1, CountModules(station.InstalledModules, "refinery-stack")); return 1f;
} }
if (recipe.RequiredModules.Contains("fabricator-array", StringComparer.Ordinal)) return Math.Max(1, CountModules(station.InstalledModules, laneModuleId));
{
return Math.Max(1, CountModules(station.InstalledModules, "fabricator-array"));
}
if (recipe.RequiredModules.Contains("component-factory", StringComparer.Ordinal))
{
return Math.Max(1, CountModules(station.InstalledModules, "component-factory"));
}
if (recipe.RequiredModules.Contains("ship-factory", StringComparer.Ordinal))
{
return Math.Max(1, CountModules(station.InstalledModules, "ship-factory"));
}
return 1f;
} }
private sealed record DesiredMarketOrder(string Kind, string ItemId, float Amount, float Valuation, float? ReserveThreshold); private sealed record DesiredMarketOrder(string Kind, string ItemId, float Amount, float Valuation, float? ReserveThreshold);

View File

@@ -63,6 +63,7 @@
"name": "Refinery Stack", "name": "Refinery Stack",
"description": "Heavy refining line for ore to refined metals.", "description": "Heavy refining line for ore to refined metals.",
"type": "Production", "type": "Production",
"productionMode": "passive",
"product": "refined-metals", "product": "refined-metals",
"hull": 180, "hull": 180,
"workforceNeeded": 18, "workforceNeeded": 18,
@@ -81,6 +82,7 @@
"name": "Solar Array", "name": "Solar Array",
"description": "Orbital solar generation and utility frame.", "description": "Orbital solar generation and utility frame.",
"type": "Production", "type": "Production",
"productionMode": "passive",
"hull": 110, "hull": 110,
"workforceNeeded": 6, "workforceNeeded": 6,
"construction": { "construction": {
@@ -98,6 +100,7 @@
"name": "Fabricator Array", "name": "Fabricator Array",
"description": "General fabrication line for industrial goods and prefab kits.", "description": "General fabrication line for industrial goods and prefab kits.",
"type": "Build Module", "type": "Build Module",
"productionMode": "commanded",
"hull": 200, "hull": 200,
"workforceNeeded": 20, "workforceNeeded": 20,
"construction": { "construction": {
@@ -115,6 +118,7 @@
"name": "Component Factory", "name": "Component Factory",
"description": "Assembly line for ship-grade modules and integrated components.", "description": "Assembly line for ship-grade modules and integrated components.",
"type": "Build Module", "type": "Build Module",
"productionMode": "commanded",
"hull": 220, "hull": 220,
"workforceNeeded": 24, "workforceNeeded": 24,
"construction": { "construction": {
@@ -136,6 +140,7 @@
"name": "Ship Factory", "name": "Ship Factory",
"description": "Slip-line and integration yard for final ship assembly.", "description": "Slip-line and integration yard for final ship assembly.",
"type": "Build Module", "type": "Build Module",
"productionMode": "commanded",
"hull": 260, "hull": 260,
"workforceNeeded": 28, "workforceNeeded": 28,
"construction": { "construction": {