Add fuel logistics, modular construction, and pad docking

This commit is contained in:
2026-03-13 15:21:16 -04:00
parent 95dd550fdb
commit bf744ec43e
16 changed files with 1128 additions and 282 deletions

View File

@@ -86,6 +86,7 @@ public sealed record StationSnapshot(
Vector3Dto LocalPosition,
string Color,
int DockedShips,
int DockingPads,
float EnergyStored,
IReadOnlyList<InventoryEntry> Inventory,
string FactionId);
@@ -98,6 +99,7 @@ public sealed record StationDelta(
Vector3Dto LocalPosition,
string Color,
int DockedShips,
int DockingPads,
float EnergyStored,
IReadOnlyList<InventoryEntry> Inventory,
string FactionId);

View File

@@ -5,8 +5,10 @@ public sealed class BalanceDefinition
public float YPlane { get; set; }
public float ArrivalThreshold { get; set; }
public float MiningRate { get; set; }
public float MiningCycleSeconds { get; set; }
public float TransferRate { get; set; }
public float DockingDuration { get; set; }
public float UndockingDuration { get; set; }
public float UndockDistance { get; set; }
public EnergyBalanceDefinition Energy { get; set; } = new();
public FuelBalanceDefinition Fuel { get; set; } = new();
@@ -68,6 +70,19 @@ public sealed class ItemDefinition
public string Summary { get; set; } = string.Empty;
}
public sealed class RecipeInputDefinition
{
public required string ItemId { get; set; }
public float Amount { get; set; }
}
public sealed class ModuleRecipeDefinition
{
public required string ModuleId { get; set; }
public float Duration { get; set; }
public required List<RecipeInputDefinition> Inputs { get; set; }
}
public sealed class PlanetDefinition
{
public required string Label { get; set; }

View File

@@ -14,6 +14,7 @@ public sealed class SimulationWorld
public required List<FactionRuntime> Factions { get; init; }
public required Dictionary<string, ShipDefinition> ShipDefinitions { get; init; }
public required Dictionary<string, ItemDefinition> ItemDefinitions { get; init; }
public required Dictionary<string, ModuleRecipeDefinition> ModuleRecipes { get; init; }
public int TickIntervalMs { get; init; } = 200;
public DateTimeOffset GeneratedAtUtc { get; set; }
}
@@ -43,13 +44,24 @@ public sealed class StationRuntime
public required ConstructibleDefinition Definition { get; init; }
public required Vector3 Position { get; init; }
public required string FactionId { get; init; }
public HashSet<string> InstalledModules { get; } = new(StringComparer.Ordinal);
public Dictionary<string, float> Inventory { get; } = new(StringComparer.Ordinal);
public Dictionary<int, string> DockingPadAssignments { get; } = new();
public float EnergyStored { get; set; }
public float ProcessTimer { get; set; }
public HashSet<string> DockedShipIds { get; } = [];
public ModuleConstructionRuntime? ActiveConstruction { get; set; }
public string LastDeltaSignature { get; set; } = string.Empty;
}
public sealed class ModuleConstructionRuntime
{
public required string ModuleId { get; init; }
public float ProgressSeconds { get; set; }
public float RequiredSeconds { get; init; }
public string AssignedConstructorShipId { get; set; } = string.Empty;
}
public sealed class ShipRuntime
{
public required string Id { get; init; }
@@ -67,6 +79,7 @@ public sealed class ShipRuntime
public Dictionary<string, float> Inventory { get; } = new(StringComparer.Ordinal);
public float EnergyStored { get; set; }
public string? DockedStationId { get; set; }
public int? AssignedDockingPadIndex { get; set; }
public float Health { get; set; }
public List<string> History { get; } = [];
public string LastSignature { get; set; } = string.Empty;
@@ -98,8 +111,10 @@ public sealed class DefaultBehaviorRuntime
{
public required string Kind { get; set; }
public string? AreaSystemId { get; set; }
public string? StationId { get; set; }
public string? RefineryId { get; set; }
public string? NodeId { get; set; }
public string? ModuleId { get; set; }
public string? Phase { get; set; }
public List<Vector3> PatrolPoints { get; set; } = [];
public int PatrolIndex { get; set; }

View File

@@ -88,11 +88,13 @@ public sealed class ScenarioLoader
var ships = Read<List<ShipDefinition>>("ships.json");
var constructibles = Read<List<ConstructibleDefinition>>("constructibles.json");
var items = Read<List<ItemDefinition>>("items.json");
var moduleRecipes = Read<List<ModuleRecipeDefinition>>("module-recipes.json");
var balance = Read<BalanceDefinition>("balance.json");
var shipDefinitions = ships.ToDictionary((definition) => definition.Id, StringComparer.Ordinal);
var constructibleDefinitions = constructibles.ToDictionary((definition) => definition.Id, StringComparer.Ordinal);
var itemDefinitions = items.ToDictionary((definition) => definition.Id, StringComparer.Ordinal);
var moduleRecipeDefinitions = moduleRecipes.ToDictionary((definition) => definition.ModuleId, StringComparer.Ordinal);
var systemRuntimes = systems
.Select((definition) => new SystemRuntime
{
@@ -141,17 +143,23 @@ public sealed class ScenarioLoader
Position = ResolveStationPosition(system, plan, balance),
FactionId = plan.FactionId ?? DefaultFactionId,
});
foreach (var moduleId in definition.Modules)
{
stations[^1].InstalledModules.Add(moduleId);
}
}
foreach (var station in stations)
{
station.Inventory["gas"] = 320f;
station.Inventory["fuel"] = 240f;
station.Inventory["refined-metals"] = 120f;
}
var refinery = stations.FirstOrDefault((station) =>
HasModules(station.Definition, "refinery-stack", "power-core", "liquid-tank", "gas-tank") &&
HasInstalledModules(station, "power-core", "liquid-tank") &&
station.SystemId == scenario.MiningDefaults.RefinerySystemId)
?? stations.FirstOrDefault((station) => HasModules(station.Definition, "refinery-stack", "power-core", "liquid-tank", "gas-tank"));
?? stations.FirstOrDefault((station) => HasInstalledModules(station, "power-core", "liquid-tank"));
var patrolRoutes = scenario.PatrolRoutes.ToDictionary(
(route) => route.SystemId,
@@ -185,9 +193,15 @@ public sealed class ScenarioLoader
});
shipsRuntime[^1].Inventory["gas"] = definition.Id switch
{
_ => 0f,
};
shipsRuntime[^1].Inventory.Remove("gas");
shipsRuntime[^1].Inventory["fuel"] = definition.Id switch
{
"constructor" => 90f,
"miner" => 90f,
"gas-miner" => 90f,
_ => 120f,
};
}
@@ -208,6 +222,7 @@ public sealed class ScenarioLoader
Factions = factions,
ShipDefinitions = shipDefinitions,
ItemDefinitions = itemDefinitions,
ModuleRecipes = moduleRecipeDefinitions,
GeneratedAtUtc = DateTimeOffset.UtcNow,
};
}
@@ -818,13 +833,33 @@ public sealed class ScenarioLoader
IReadOnlyDictionary<string, List<Vector3>> patrolRoutes,
StationRuntime? refinery)
{
if (HasModules(definition, "fabricator-array", "docking-clamps") && refinery is not null)
{
return new DefaultBehaviorRuntime
{
Kind = "construct-station",
StationId = refinery.Id,
Phase = "travel-to-station",
};
}
if (HasModules(definition, "reactor-core", "capacitor-bank", "gas-extractor") && refinery is not null)
{
return new DefaultBehaviorRuntime
{
Kind = "auto-harvest-gas",
StationId = refinery.Id,
Phase = "travel-to-node",
};
}
if (HasModules(definition, "reactor-core", "capacitor-bank", "mining-turret") && refinery is not null)
{
return new DefaultBehaviorRuntime
{
Kind = "auto-mine",
AreaSystemId = scenario.MiningDefaults.NodeSystemId,
RefineryId = refinery.Id,
StationId = refinery.Id,
Phase = "travel-to-node",
};
}
@@ -883,6 +918,9 @@ public sealed class ScenarioLoader
private static bool HasModules(ConstructibleDefinition definition, params string[] modules) =>
modules.All((moduleId) => definition.Modules.Contains(moduleId, StringComparer.Ordinal));
private static bool HasInstalledModules(StationRuntime station, params string[] modules) =>
modules.All((moduleId) => station.InstalledModules.Contains(moduleId, StringComparer.Ordinal));
private static bool HasModules(ShipDefinition definition, params string[] modules) =>
modules.All((moduleId) => definition.Modules.Contains(moduleId, StringComparer.Ordinal));

File diff suppressed because it is too large Load Diff