feat: simplifying the simulation
This commit is contained in:
@@ -3,8 +3,8 @@ namespace SpaceGame.Simulation.Api.Contracts;
|
||||
public sealed record ShipSnapshot(
|
||||
string Id,
|
||||
string Label,
|
||||
string Role,
|
||||
string ShipClass,
|
||||
string Kind,
|
||||
string Class,
|
||||
string SystemId,
|
||||
Vector3Dto LocalPosition,
|
||||
Vector3Dto LocalVelocity,
|
||||
@@ -19,8 +19,7 @@ public sealed record ShipSnapshot(
|
||||
string? CommanderId,
|
||||
string? PolicySetId,
|
||||
float CargoCapacity,
|
||||
string? CargoItemId,
|
||||
float WorkerPopulation,
|
||||
|
||||
float TravelSpeed,
|
||||
string TravelSpeedUnit,
|
||||
IReadOnlyList<InventoryEntry> Inventory,
|
||||
@@ -33,8 +32,8 @@ public sealed record ShipSnapshot(
|
||||
public sealed record ShipDelta(
|
||||
string Id,
|
||||
string Label,
|
||||
string Role,
|
||||
string ShipClass,
|
||||
string Kind,
|
||||
string Class,
|
||||
string SystemId,
|
||||
Vector3Dto LocalPosition,
|
||||
Vector3Dto LocalVelocity,
|
||||
@@ -49,8 +48,7 @@ public sealed record ShipDelta(
|
||||
string? CommanderId,
|
||||
string? PolicySetId,
|
||||
float CargoCapacity,
|
||||
string? CargoItemId,
|
||||
float WorkerPopulation,
|
||||
|
||||
float TravelSpeed,
|
||||
string TravelSpeedUnit,
|
||||
IReadOnlyList<InventoryEntry> Inventory,
|
||||
|
||||
@@ -147,20 +147,19 @@ public sealed class ShipDefinition
|
||||
{
|
||||
public required string Id { get; set; }
|
||||
public required string Label { get; set; }
|
||||
public required string Role { get; set; }
|
||||
public required string ShipClass { get; set; }
|
||||
public required string Kind { get; set; }
|
||||
public required string Class { get; set; }
|
||||
public float Speed { get; set; }
|
||||
public float WarpSpeed { get; set; }
|
||||
public float FtlSpeed { get; set; }
|
||||
public float SpoolTime { get; set; }
|
||||
public float CargoCapacity { get; set; }
|
||||
public string? CargoKind { get; set; }
|
||||
public string? CargoItemId { get; set; }
|
||||
public required string Color { get; set; }
|
||||
public required string HullColor { get; set; }
|
||||
public float Size { get; set; }
|
||||
public float MaxHealth { get; set; }
|
||||
public List<string> Modules { get; set; } = [];
|
||||
public List<string> Capabilities { get; set; } = [];
|
||||
public ConstructionDefinition? Construction { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ internal sealed class ShipBehaviorStateMachine
|
||||
{
|
||||
idleState,
|
||||
new PatrolShipBehaviorState(),
|
||||
new ResourceHarvestShipBehaviorState("auto-mine", "ore", "mining-turret"),
|
||||
new ResourceHarvestShipBehaviorState("auto-mine", "ore", "mining"),
|
||||
new ConstructStationShipBehaviorState(),
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed class ShipRuntime
|
||||
public required ControllerTaskRuntime ControllerTask { get; set; }
|
||||
public float ActionTimer { get; set; }
|
||||
public Dictionary<string, float> Inventory { get; } = new(StringComparer.Ordinal);
|
||||
public float WorkerPopulation { get; set; }
|
||||
|
||||
public string DockedStationId { get; set; }
|
||||
public int? AssignedDockingPadIndex { get; set; }
|
||||
public string? CommanderId { get; set; }
|
||||
@@ -62,4 +62,5 @@ public sealed class ControllerTaskRuntime
|
||||
public string? TargetNodeId { get; set; }
|
||||
public Vector3? TargetPosition { get; set; }
|
||||
public float Threshold { get; set; }
|
||||
public string? ItemId { get; set; }
|
||||
}
|
||||
|
||||
@@ -62,8 +62,7 @@ public enum ControllerTaskKind
|
||||
Unload,
|
||||
DeliverConstruction,
|
||||
BuildConstructionSite,
|
||||
LoadWorkers,
|
||||
UnloadWorkers,
|
||||
|
||||
ConstructModule,
|
||||
Undock,
|
||||
}
|
||||
@@ -105,8 +104,7 @@ public static class ShipTaskKinds
|
||||
public const string Undock = "undock";
|
||||
public const string LoadCargo = "load-cargo";
|
||||
public const string UnloadCargo = "unload-cargo";
|
||||
public const string LoadWorkers = "load-workers";
|
||||
public const string UnloadWorkers = "unload-workers";
|
||||
|
||||
public const string MineNode = "mine-node";
|
||||
public const string HarvestGas = "harvest-gas";
|
||||
public const string DeliverToStation = "deliver-to-station";
|
||||
@@ -229,8 +227,7 @@ public static class SimulationEnumMappings
|
||||
ControllerTaskKind.Unload => "unload",
|
||||
ControllerTaskKind.DeliverConstruction => "deliver-construction",
|
||||
ControllerTaskKind.BuildConstructionSite => "build-construction-site",
|
||||
ControllerTaskKind.LoadWorkers => "load-workers",
|
||||
ControllerTaskKind.UnloadWorkers => "unload-workers",
|
||||
|
||||
ControllerTaskKind.ConstructModule => "construct-module",
|
||||
ControllerTaskKind.Undock => "undock",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null),
|
||||
|
||||
@@ -379,7 +379,7 @@ public sealed partial class ScenarioLoader
|
||||
IReadOnlyDictionary<string, List<Vector3>> patrolRoutes,
|
||||
StationRuntime? refinery)
|
||||
{
|
||||
if (string.Equals(definition.Role, "construction", StringComparison.Ordinal) && refinery is not null)
|
||||
if (string.Equals(definition.Kind, "construction", StringComparison.Ordinal) && refinery is not null)
|
||||
{
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
@@ -389,12 +389,12 @@ public sealed partial class ScenarioLoader
|
||||
};
|
||||
}
|
||||
|
||||
if (HasModules(definition, "reactor-core", "capacitor-bank", "mining-turret") && refinery is not null)
|
||||
if (HasCapabilities(definition, "mining") && refinery is not null)
|
||||
{
|
||||
return CreateResourceHarvestBehavior("auto-mine", scenario.MiningDefaults.NodeSystemId, refinery.Id);
|
||||
}
|
||||
|
||||
if (HasModules(definition, "reactor-core", "capacitor-bank", "gun-turret") && patrolRoutes.TryGetValue(systemId, out var route))
|
||||
if (string.Equals(definition.Kind, "military", StringComparison.Ordinal) && patrolRoutes.TryGetValue(systemId, out var route))
|
||||
{
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
|
||||
@@ -400,8 +400,8 @@ public sealed partial class ScenarioLoader
|
||||
private static bool HasInstalledModules(StationRuntime station, params string[] modules) =>
|
||||
modules.All((moduleId) => station.Modules.Any((candidate) => string.Equals(candidate.ModuleId, moduleId, StringComparison.Ordinal)));
|
||||
|
||||
private static bool HasModules(ShipDefinition definition, params string[] modules) =>
|
||||
modules.All((moduleId) => definition.Modules.Contains(moduleId, StringComparer.Ordinal));
|
||||
private static bool HasCapabilities(ShipDefinition definition, params string[] capabilities) =>
|
||||
capabilities.All((cap) => definition.Capabilities.Contains(cap, StringComparer.Ordinal));
|
||||
|
||||
private static void AddStationModule(StationRuntime station, IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions, string moduleId)
|
||||
{
|
||||
|
||||
@@ -27,8 +27,7 @@ public sealed partial class SimulationEngine
|
||||
ControllerTaskKind.Unload => UpdateUnload(ship, world, deltaSeconds),
|
||||
ControllerTaskKind.DeliverConstruction => UpdateDeliverConstruction(ship, world, deltaSeconds),
|
||||
ControllerTaskKind.BuildConstructionSite => UpdateBuildConstructionSite(ship, world, deltaSeconds),
|
||||
ControllerTaskKind.LoadWorkers => UpdateLoadWorkers(ship, world, deltaSeconds),
|
||||
ControllerTaskKind.UnloadWorkers => UpdateUnloadWorkers(ship, world, deltaSeconds),
|
||||
|
||||
ControllerTaskKind.ConstructModule => UpdateConstructModule(ship, world, deltaSeconds),
|
||||
ControllerTaskKind.Undock => UpdateUndock(ship, world, deltaSeconds),
|
||||
_ => UpdateIdle(ship, world, deltaSeconds),
|
||||
@@ -58,6 +57,12 @@ public sealed partial class SimulationEngine
|
||||
|
||||
if (ship.SystemId != task.TargetSystemId)
|
||||
{
|
||||
if (!HasShipCapabilities(ship.Definition, "ftl"))
|
||||
{
|
||||
ship.State = ShipState.Idle;
|
||||
return "none";
|
||||
}
|
||||
|
||||
var destinationEntryNode = ResolveSystemEntryNode(world, task.TargetSystemId);
|
||||
var destinationEntryPosition = destinationEntryNode?.Position ?? Vector3.Zero;
|
||||
return UpdateFtlTransit(ship, world, deltaSeconds, task.TargetSystemId, destinationEntryPosition, destinationEntryNode);
|
||||
@@ -66,6 +71,11 @@ public sealed partial class SimulationEngine
|
||||
var currentNode = ResolveCurrentNode(world, ship);
|
||||
if (targetNode is not null && currentNode is not null && !string.Equals(currentNode.Id, targetNode.Id, StringComparison.Ordinal))
|
||||
{
|
||||
if (!HasShipCapabilities(ship.Definition, "warp"))
|
||||
{
|
||||
return UpdateLocalTravel(ship, world, deltaSeconds, task.TargetSystemId, targetPosition, targetNode, task.Threshold);
|
||||
}
|
||||
|
||||
return UpdateWarpTransit(ship, world, deltaSeconds, targetPosition, targetNode);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,8 @@ namespace SpaceGame.Simulation.Api.Simulation;
|
||||
|
||||
public sealed partial class SimulationEngine
|
||||
{
|
||||
private static bool HasShipModules(ShipDefinition definition, params string[] modules) =>
|
||||
modules.All(moduleId => definition.Modules.Contains(moduleId, StringComparer.Ordinal));
|
||||
|
||||
private static bool CanTransportWorkers(ShipRuntime ship) =>
|
||||
CountModules(ship.Definition.Modules, "habitat-ring") > 0;
|
||||
|
||||
private static float GetWorkerTransportCapacity(ShipRuntime ship) =>
|
||||
CountModules(ship.Definition.Modules, "habitat-ring") * 120f;
|
||||
private static bool HasShipCapabilities(ShipDefinition definition, params string[] capabilities) =>
|
||||
capabilities.All(cap => definition.Capabilities.Contains(cap, StringComparer.Ordinal));
|
||||
|
||||
private static int CountStationModules(StationRuntime station, string moduleId) =>
|
||||
station.Modules.Count(module => string.Equals(module.ModuleId, moduleId, StringComparison.Ordinal));
|
||||
@@ -56,8 +50,8 @@ public sealed partial class SimulationEngine
|
||||
|
||||
var moduleCapacity = storageClass switch
|
||||
{
|
||||
"bulk-solid" => bulkBays * 1000f,
|
||||
"bulk-liquid" => liquidTanks * 500f,
|
||||
"solid" => bulkBays * 1000f,
|
||||
"liquid" => liquidTanks * 500f,
|
||||
"container" => containerBays * 800f,
|
||||
"manufactured" => containerBays * 200f,
|
||||
_ => 0f,
|
||||
@@ -102,15 +96,13 @@ public sealed partial class SimulationEngine
|
||||
private static bool HasStationModules(StationRuntime station, params string[] modules) =>
|
||||
modules.All(moduleId => station.Modules.Any(candidate => string.Equals(candidate.ModuleId, moduleId, StringComparison.Ordinal)));
|
||||
|
||||
private static bool CanExtractNode(ShipRuntime ship, ResourceNodeRuntime node) =>
|
||||
node.ItemId switch
|
||||
{
|
||||
"ore" => HasShipModules(ship.Definition, "mining-turret"),
|
||||
_ => false,
|
||||
};
|
||||
private static bool CanExtractNode(ShipRuntime ship, ResourceNodeRuntime node, SimulationWorld world) =>
|
||||
HasShipCapabilities(ship.Definition, "mining")
|
||||
&& world.ItemDefinitions.TryGetValue(node.ItemId, out var item)
|
||||
&& string.Equals(item.CargoKind, ship.Definition.CargoKind, StringComparison.Ordinal);
|
||||
|
||||
private static bool CanBuildClaimBeacon(ShipRuntime ship) =>
|
||||
string.Equals(ship.Definition.Role, "military", StringComparison.Ordinal);
|
||||
string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal);
|
||||
|
||||
private static float ComputeWorkforceRatio(float population, float workforceRequired)
|
||||
{
|
||||
@@ -126,8 +118,8 @@ public sealed partial class SimulationEngine
|
||||
private static string? GetStorageRequirement(string storageClass) =>
|
||||
storageClass switch
|
||||
{
|
||||
"bulk-solid" => "bulk-bay",
|
||||
"bulk-liquid" => "liquid-tank",
|
||||
"solid" => "bulk-bay",
|
||||
"liquid" => "liquid-tank",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
|
||||
@@ -143,8 +143,8 @@ public sealed partial class SimulationEngine
|
||||
world.Ships.Select(ship => ToShipDelta(world, ship)).Select(ship => new ShipSnapshot(
|
||||
ship.Id,
|
||||
ship.Label,
|
||||
ship.Role,
|
||||
ship.ShipClass,
|
||||
ship.Kind,
|
||||
ship.Class,
|
||||
ship.SystemId,
|
||||
ship.LocalPosition,
|
||||
ship.LocalVelocity,
|
||||
@@ -159,8 +159,6 @@ public sealed partial class SimulationEngine
|
||||
ship.CommanderId,
|
||||
ship.PolicySetId,
|
||||
ship.CargoCapacity,
|
||||
ship.CargoItemId,
|
||||
ship.WorkerPopulation,
|
||||
ship.TravelSpeed,
|
||||
ship.TravelSpeedUnit,
|
||||
ship.Inventory,
|
||||
@@ -482,7 +480,6 @@ public sealed partial class SimulationEngine
|
||||
ship.DockedStationId ?? "none",
|
||||
ship.CommanderId ?? "none",
|
||||
ship.PolicySetId ?? "none",
|
||||
ship.WorkerPopulation.ToString("0.###"),
|
||||
ship.SpatialState.SpaceLayer,
|
||||
ship.SpatialState.CurrentNodeId ?? "none",
|
||||
ship.SpatialState.CurrentBubbleId ?? "none",
|
||||
@@ -586,7 +583,7 @@ public sealed partial class SimulationEngine
|
||||
|
||||
private static IReadOnlyList<StationStorageUsageSnapshot> ToStationStorageUsageSnapshots(SimulationWorld world, StationRuntime station)
|
||||
{
|
||||
string[] storageClasses = ["bulk-solid", "bulk-liquid", "container", "manufactured"];
|
||||
string[] storageClasses = ["solid", "liquid", "container", "manufactured"];
|
||||
return storageClasses
|
||||
.Select(storageClass => new StationStorageUsageSnapshot(
|
||||
storageClass,
|
||||
@@ -666,8 +663,8 @@ public sealed partial class SimulationEngine
|
||||
private ShipDelta ToShipDelta(SimulationWorld world, ShipRuntime ship) => new(
|
||||
ship.Id,
|
||||
ship.Definition.Label,
|
||||
ship.Definition.Role,
|
||||
ship.Definition.ShipClass,
|
||||
ship.Definition.Kind,
|
||||
ship.Definition.Class,
|
||||
ship.SystemId,
|
||||
ToDto(ship.Position),
|
||||
ToDto(ship.Velocity),
|
||||
@@ -682,8 +679,7 @@ public sealed partial class SimulationEngine
|
||||
ship.CommanderId,
|
||||
ship.PolicySetId,
|
||||
ship.Definition.CargoCapacity,
|
||||
ship.Definition.CargoItemId,
|
||||
ship.WorkerPopulation,
|
||||
|
||||
ToShipTravelSpeed(ship).Speed,
|
||||
ToShipTravelSpeed(ship).Unit,
|
||||
ToInventoryEntries(ship.Inventory),
|
||||
@@ -705,14 +701,7 @@ public sealed partial class SimulationEngine
|
||||
ShipState.Docking => CreateShipActionProgress("Docking", ship.ActionTimer, MathF.Max(world.Balance.DockingDuration, 0.1f)),
|
||||
ShipState.Undocking => CreateShipActionProgress("Undocking", ship.ActionTimer, MathF.Max(world.Balance.UndockingDuration, 0.1f)),
|
||||
ShipState.Transferring => CreateShipRemainingActionProgress("Transfer", ship.TrackedActionTotal, GetShipCargoAmount(ship)),
|
||||
ShipState.Loading => CreateShipRemainingActionProgress(
|
||||
"Load workers",
|
||||
ship.TrackedActionTotal,
|
||||
MathF.Max(0f, ship.TrackedActionTotal - ship.WorkerPopulation)),
|
||||
ShipState.Unloading => CreateShipRemainingActionProgress(
|
||||
"Unload workers",
|
||||
ship.TrackedActionTotal,
|
||||
ship.WorkerPopulation),
|
||||
ShipState.Loading or ShipState.Unloading => null,
|
||||
ShipState.DeliveringConstruction => ship.ControllerTask.TargetEntityId is null
|
||||
? null
|
||||
: world.ConstructionSites.FirstOrDefault(site => site.Id == ship.ControllerTask.TargetEntityId) is not { } site
|
||||
|
||||
@@ -106,10 +106,38 @@ public sealed partial class SimulationEngine
|
||||
|
||||
private static string? GetNextStationModuleToBuild(StationRuntime station, SimulationWorld world)
|
||||
{
|
||||
// Expand storage before it becomes a bottleneck
|
||||
const float StorageExpansionThreshold = 0.85f;
|
||||
var storageExpansionCandidates = new[]
|
||||
{
|
||||
("solid", "bulk-bay"),
|
||||
("liquid", "liquid-tank"),
|
||||
("container", "container-bay"),
|
||||
};
|
||||
|
||||
foreach (var (storageClass, moduleId) in storageExpansionCandidates)
|
||||
{
|
||||
var capacity = GetStationStorageCapacity(station, storageClass);
|
||||
if (capacity <= 0.01f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var used = station.Inventory
|
||||
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoKind == storageClass)
|
||||
.Sum(entry => entry.Value);
|
||||
|
||||
if (used / capacity >= StorageExpansionThreshold && world.ModuleRecipes.ContainsKey(moduleId))
|
||||
{
|
||||
return moduleId;
|
||||
}
|
||||
}
|
||||
|
||||
var priorities = GetFactionExpansionPressure(world, station.FactionId) > 0f
|
||||
? new (string ModuleId, int TargetCount)[]
|
||||
{
|
||||
("refinery-stack", 1),
|
||||
("bulk-bay", 1),
|
||||
("container-bay", 1),
|
||||
("fabricator-array", 2),
|
||||
("component-factory", 1),
|
||||
@@ -120,6 +148,7 @@ public sealed partial class SimulationEngine
|
||||
: new (string ModuleId, int TargetCount)[]
|
||||
{
|
||||
("refinery-stack", 1),
|
||||
("bulk-bay", 1),
|
||||
("container-bay", 1),
|
||||
("fabricator-array", 2),
|
||||
("component-factory", 1),
|
||||
|
||||
@@ -25,17 +25,14 @@ public sealed partial class SimulationEngine
|
||||
ship.TrackedActionTotal = MathF.Max(total, 0.01f);
|
||||
}
|
||||
|
||||
internal static float GetShipCargoAmount(ShipRuntime ship)
|
||||
{
|
||||
var cargoItemId = ship.Definition.CargoItemId;
|
||||
return cargoItemId is null ? 0f : GetInventoryAmount(ship.Inventory, cargoItemId);
|
||||
}
|
||||
internal static float GetShipCargoAmount(ShipRuntime ship) =>
|
||||
ship.Inventory.Values.Sum();
|
||||
|
||||
private string UpdateExtract(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
||||
{
|
||||
var task = ship.ControllerTask;
|
||||
var node = world.Nodes.FirstOrDefault(candidate => candidate.Id == task.TargetEntityId);
|
||||
if (node is null || task.TargetPosition is null || !CanExtractNode(ship, node))
|
||||
if (node is null || task.TargetPosition is null || !CanExtractNode(ship, node, world))
|
||||
{
|
||||
ship.State = ShipState.Idle;
|
||||
ship.TargetPosition = ship.Position;
|
||||
@@ -79,10 +76,7 @@ public sealed partial class SimulationEngine
|
||||
return node.OreRemaining <= 0.01f ? "node-depleted" : "cargo-full";
|
||||
}
|
||||
|
||||
if (ship.Definition.CargoItemId is not null)
|
||||
{
|
||||
AddInventory(ship.Inventory, ship.Definition.CargoItemId, mined);
|
||||
}
|
||||
AddInventory(ship.Inventory, node.ItemId, mined);
|
||||
|
||||
node.OreRemaining -= mined;
|
||||
node.OreRemaining = MathF.Max(0f, node.OreRemaining);
|
||||
@@ -167,23 +161,21 @@ public sealed partial class SimulationEngine
|
||||
ship.ActionTimer = 0f;
|
||||
ship.State = ShipState.Transferring;
|
||||
BeginTrackedAction(ship, "transferring", GetShipCargoAmount(ship));
|
||||
var cargoItemId = ship.Definition.CargoItemId;
|
||||
var moved = cargoItemId is null ? 0f : MathF.Min(GetInventoryAmount(ship.Inventory, cargoItemId), world.Balance.TransferRate * deltaSeconds);
|
||||
if (cargoItemId is not null)
|
||||
{
|
||||
var accepted = TryAddStationInventory(world, station, cargoItemId, moved);
|
||||
RemoveInventory(ship.Inventory, cargoItemId, accepted);
|
||||
moved = accepted;
|
||||
}
|
||||
|
||||
var faction = world.Factions.FirstOrDefault(candidate => candidate.Id == ship.FactionId);
|
||||
if (faction is not null && cargoItemId == "ore")
|
||||
foreach (var (itemId, amount) in ship.Inventory.ToList())
|
||||
{
|
||||
faction.OreMined += moved;
|
||||
faction.Credits += moved * 0.4f;
|
||||
var moved = MathF.Min(amount, world.Balance.TransferRate * deltaSeconds);
|
||||
var accepted = TryAddStationInventory(world, station, itemId, moved);
|
||||
RemoveInventory(ship.Inventory, itemId, accepted);
|
||||
if (faction is not null && string.Equals(itemId, "ore", StringComparison.Ordinal))
|
||||
{
|
||||
faction.OreMined += accepted;
|
||||
faction.Credits += accepted * 0.4f;
|
||||
}
|
||||
}
|
||||
|
||||
return cargoItemId is null || GetInventoryAmount(ship.Inventory, cargoItemId) <= 0.01f ? "unloaded" : "none";
|
||||
return GetShipCargoAmount(ship) <= 0.01f ? "unloaded" : "none";
|
||||
}
|
||||
|
||||
private string UpdateLoadCargo(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
||||
@@ -209,19 +201,19 @@ public sealed partial class SimulationEngine
|
||||
ship.Position = ship.TargetPosition;
|
||||
ship.ActionTimer = 0f;
|
||||
ship.State = ShipState.Loading;
|
||||
var itemId = ship.ControllerTask.ItemId;
|
||||
BeginTrackedAction(ship, "loading", MathF.Max(0f, ship.Definition.CargoCapacity - GetShipCargoAmount(ship)));
|
||||
var cargoItemId = ship.Definition.CargoItemId;
|
||||
var transfer = MathF.Min(world.Balance.TransferRate * deltaSeconds, ship.Definition.CargoCapacity - GetShipCargoAmount(ship));
|
||||
var moved = cargoItemId is null ? 0f : MathF.Min(transfer, GetInventoryAmount(station.Inventory, cargoItemId));
|
||||
if (cargoItemId is not null && moved > 0.01f)
|
||||
var moved = itemId is null ? 0f : MathF.Min(transfer, GetInventoryAmount(station.Inventory, itemId));
|
||||
if (itemId is not null && moved > 0.01f)
|
||||
{
|
||||
RemoveInventory(station.Inventory, cargoItemId, moved);
|
||||
AddInventory(ship.Inventory, cargoItemId, moved);
|
||||
RemoveInventory(station.Inventory, itemId, moved);
|
||||
AddInventory(ship.Inventory, itemId, moved);
|
||||
}
|
||||
|
||||
return cargoItemId is null
|
||||
return itemId is null
|
||||
|| GetShipCargoAmount(ship) >= ship.Definition.CargoCapacity - 0.01f
|
||||
|| GetInventoryAmount(station.Inventory, cargoItemId) <= 0.01f
|
||||
|| GetInventoryAmount(station.Inventory, itemId) <= 0.01f
|
||||
? "loaded"
|
||||
: "none";
|
||||
}
|
||||
@@ -411,65 +403,6 @@ public sealed partial class SimulationEngine
|
||||
private static bool IsShipWithinSupportRange(ShipRuntime ship, Vector3 supportPosition, float threshold) =>
|
||||
ship.Position.DistanceTo(supportPosition) <= MathF.Max(threshold, 6f);
|
||||
|
||||
private string UpdateLoadWorkers(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
||||
{
|
||||
if (ship.DockedStationId is null || !CanTransportWorkers(ship))
|
||||
{
|
||||
ship.State = ShipState.Blocked;
|
||||
return "failed";
|
||||
}
|
||||
|
||||
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DockedStationId);
|
||||
if (station is null || station.Population <= 0.01f)
|
||||
{
|
||||
ship.State = ShipState.Idle;
|
||||
return "none";
|
||||
}
|
||||
|
||||
var transfer = MathF.Min(station.Population, GetWorkerTransportCapacity(ship) - ship.WorkerPopulation);
|
||||
var totalTransfer = MathF.Min(station.Population, GetWorkerTransportCapacity(ship) - ship.WorkerPopulation);
|
||||
transfer = MathF.Min(transfer, 4f * deltaSeconds);
|
||||
if (transfer <= 0.01f)
|
||||
{
|
||||
return "none";
|
||||
}
|
||||
|
||||
station.Population = MathF.Max(0f, station.Population - transfer);
|
||||
ship.WorkerPopulation += transfer;
|
||||
ship.State = ShipState.Loading;
|
||||
BeginTrackedAction(ship, "loading", totalTransfer);
|
||||
return ship.WorkerPopulation >= GetWorkerTransportCapacity(ship) - 0.01f ? "workers-loaded" : "none";
|
||||
}
|
||||
|
||||
private string UpdateUnloadWorkers(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
||||
{
|
||||
if (ship.DockedStationId is null || !CanTransportWorkers(ship))
|
||||
{
|
||||
ship.State = ShipState.Blocked;
|
||||
return "failed";
|
||||
}
|
||||
|
||||
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DockedStationId);
|
||||
if (station is null || ship.WorkerPopulation <= 0.01f)
|
||||
{
|
||||
ship.State = ShipState.Idle;
|
||||
return "none";
|
||||
}
|
||||
|
||||
var transfer = MathF.Min(ship.WorkerPopulation, MathF.Max(0f, station.PopulationCapacity - station.Population));
|
||||
var totalTransfer = transfer;
|
||||
transfer = MathF.Min(transfer, 4f * deltaSeconds);
|
||||
if (transfer <= 0.01f)
|
||||
{
|
||||
return "none";
|
||||
}
|
||||
|
||||
ship.WorkerPopulation = MathF.Max(0f, ship.WorkerPopulation - transfer);
|
||||
station.Population = MathF.Min(station.PopulationCapacity, station.Population + transfer);
|
||||
ship.State = ShipState.Unloading;
|
||||
BeginTrackedAction(ship, "unloading", totalTransfer);
|
||||
return ship.WorkerPopulation <= 0.01f ? "workers-unloaded" : "none";
|
||||
}
|
||||
|
||||
private string UpdateUndock(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
||||
{
|
||||
|
||||
@@ -152,7 +152,7 @@ public sealed partial class SimulationEngine
|
||||
.FirstOrDefault()
|
||||
: world.Nodes.FirstOrDefault(candidate => candidate.Id == behavior.NodeId && candidate.OreRemaining > 0.01f);
|
||||
|
||||
if (refinery is null || node is null || !HasShipModules(ship.Definition, "reactor-core", "capacitor-bank", requiredModule))
|
||||
if (refinery is null || node is null || !HasShipCapabilities(ship.Definition, requiredModule))
|
||||
{
|
||||
behavior.Kind = "idle";
|
||||
ship.ControllerTask = CreateIdleTask(world.Balance.ArrivalThreshold);
|
||||
@@ -505,8 +505,7 @@ public sealed partial class SimulationEngine
|
||||
"unload" => ControllerTaskKind.Unload,
|
||||
"deliver-construction" => ControllerTaskKind.DeliverConstruction,
|
||||
"build-construction-site" => ControllerTaskKind.BuildConstructionSite,
|
||||
"load-workers" => ControllerTaskKind.LoadWorkers,
|
||||
"unload-workers" => ControllerTaskKind.UnloadWorkers,
|
||||
|
||||
"construct-module" => ControllerTaskKind.ConstructModule,
|
||||
"undock" => ControllerTaskKind.Undock,
|
||||
_ => ControllerTaskKind.Idle,
|
||||
|
||||
@@ -235,7 +235,7 @@ public sealed partial class SimulationEngine
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.Equals(shipDefinition.Role, "military", StringComparison.Ordinal)
|
||||
if (!string.Equals(shipDefinition.Kind, "military", StringComparison.Ordinal)
|
||||
|| !FactionNeedsMoreWarships(world, station.FactionId))
|
||||
{
|
||||
return false;
|
||||
@@ -394,7 +394,7 @@ public sealed partial class SimulationEngine
|
||||
{
|
||||
var militaryShipCount = world.Ships.Count(ship =>
|
||||
ship.FactionId == factionId
|
||||
&& string.Equals(ship.Definition.Role, "military", StringComparison.Ordinal));
|
||||
&& string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal));
|
||||
var targetSystems = Math.Max(1, Math.Min(StrategicControlTargetSystems, world.Systems.Count));
|
||||
var controlledSystems = GetFactionControlledSystemsCount(world, factionId);
|
||||
var expansionDeficit = Math.Max(0, targetSystems - controlledSystems);
|
||||
@@ -448,7 +448,7 @@ public sealed partial class SimulationEngine
|
||||
|
||||
private static DefaultBehaviorRuntime CreateSpawnedShipBehavior(ShipDefinition definition, StationRuntime station)
|
||||
{
|
||||
if (!string.Equals(definition.Role, "military", StringComparison.Ordinal))
|
||||
if (!string.Equals(definition.Kind, "military", StringComparison.Ordinal))
|
||||
{
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
|
||||
@@ -3,8 +3,8 @@ import type { InventoryEntry, Vector3Dto } from "./contractsCommon";
|
||||
export interface ShipSnapshot {
|
||||
id: string;
|
||||
label: string;
|
||||
role: string;
|
||||
shipClass: string;
|
||||
kind: string;
|
||||
class: string;
|
||||
systemId: string;
|
||||
localPosition: Vector3Dto;
|
||||
localVelocity: Vector3Dto;
|
||||
@@ -19,8 +19,7 @@ export interface ShipSnapshot {
|
||||
commanderId?: string | null;
|
||||
policySetId?: string | null;
|
||||
cargoCapacity: number;
|
||||
cargoItemId?: string | null;
|
||||
workerPopulation: number;
|
||||
|
||||
travelSpeed: number;
|
||||
travelSpeedUnit: string;
|
||||
inventory: InventoryEntry[];
|
||||
|
||||
@@ -26,9 +26,7 @@ export function renderFactionStrip(
|
||||
|
||||
return ships
|
||||
.map((ship) => {
|
||||
const cargo = ship.cargoItemId
|
||||
? inventoryAmount(ship.inventory, ship.cargoItemId)
|
||||
: 0;
|
||||
const cargo = ship.inventory.reduce((sum, e) => sum + e.amount, 0);
|
||||
const shipLocation = describeShipLocation(world, ship);
|
||||
const shipState = describeShipState(world, ship);
|
||||
const shipAction = describeShipCurrentAction(ship);
|
||||
@@ -42,7 +40,7 @@ export function renderFactionStrip(
|
||||
<div class="ship-card-header">
|
||||
<h3>${ship.label}</h3>
|
||||
<div class="ship-card-meta">
|
||||
<span class="ship-card-badge">${ship.shipClass}</span>
|
||||
<span class="ship-card-badge">${ship.class}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="ship-card-history-button"
|
||||
|
||||
@@ -196,10 +196,7 @@ export function updateDetailPanel(
|
||||
return;
|
||||
}
|
||||
const parent = describeSelectionParent(selected);
|
||||
const cargoUsed = ship.cargoItemId
|
||||
? inventoryAmount(ship.inventory, ship.cargoItemId)
|
||||
: 0;
|
||||
const cargoLabel = ship.cargoItemId ?? "none";
|
||||
const cargoUsed = ship.inventory.reduce((sum, e) => sum + e.amount, 0);
|
||||
const shipState = describeShipState(world, ship);
|
||||
const shipAction = describeShipCurrentAction(ship);
|
||||
detailTitleEl.textContent = ship.label;
|
||||
@@ -217,7 +214,7 @@ export function updateDetailPanel(
|
||||
</div>
|
||||
</div>
|
||||
` : ""}
|
||||
<p>Cargo ${cargoLabel} ${cargoUsed.toFixed(0)} / ${ship.cargoCapacity.toFixed(0)}</p>
|
||||
<p>Cargo ${cargoUsed.toFixed(0)} / ${ship.cargoCapacity.toFixed(0)}</p>
|
||||
<p>Inventory ${formatInventory(ship.inventory)}</p>
|
||||
<p>Speed ${formatShipSpeed(ship)}</p>
|
||||
<p>Camera ${cameraMode === "follow" && cameraTargetShipId === ship.id ? "camera-follow" : "tactical"}<br>Press C to toggle follow</p>
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as THREE from "three";
|
||||
import type { ShipSnapshot } from "./contracts";
|
||||
|
||||
export function shipSize(ship: ShipSnapshot) {
|
||||
switch (ship.shipClass) {
|
||||
switch (ship.class) {
|
||||
case "capital":
|
||||
return 18;
|
||||
case "cruiser":
|
||||
@@ -20,11 +20,11 @@ export function shipLength(ship: ShipSnapshot) {
|
||||
return shipSize(ship) * 2.6;
|
||||
}
|
||||
|
||||
export function shipColor(role: ShipSnapshot["role"]) {
|
||||
if (role === "mining") {
|
||||
export function shipColor(kind: ShipSnapshot["kind"]) {
|
||||
if (kind === "mining") {
|
||||
return "#ffcf6e";
|
||||
}
|
||||
if (role === "transport") {
|
||||
if (kind === "transport") {
|
||||
return "#9ff0aa";
|
||||
}
|
||||
return "#8bc0ff";
|
||||
@@ -40,7 +40,7 @@ export function shipPresentationColor(ship: ShipSnapshot) {
|
||||
if (ship.spatialState.movementRegime === "ftl-transit") {
|
||||
return "#ff6ad5";
|
||||
}
|
||||
return shipColor(ship.role);
|
||||
return shipColor(ship.kind);
|
||||
}
|
||||
|
||||
export function spatialNodeColor(kind: string) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "Raw Ore",
|
||||
"description": "Unprocessed asteroid ore used as the main industrial feedstock.",
|
||||
"type": "resource",
|
||||
"cargoKind": "bulk-solid",
|
||||
"cargoKind": "solid",
|
||||
"volume": 1.2
|
||||
},
|
||||
{
|
||||
@@ -12,12 +12,15 @@
|
||||
"name": "Water",
|
||||
"description": "Life-support and agricultural input.",
|
||||
"type": "commodity",
|
||||
"cargoKind": "bulk-liquid",
|
||||
"cargoKind": "liquid",
|
||||
"volume": 1.0,
|
||||
"construction": {
|
||||
"recipeId": "water-reclamation",
|
||||
"facilityCategory": "farm",
|
||||
"requiredModules": ["liquid-tank", "solar-array"],
|
||||
"requiredModules": [
|
||||
"liquid-tank",
|
||||
"solar-array"
|
||||
],
|
||||
"requirements": [],
|
||||
"cycleTime": 6,
|
||||
"batchSize": 12,
|
||||
@@ -36,9 +39,14 @@
|
||||
"construction": {
|
||||
"recipeId": "ore-refining",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["refinery-stack"],
|
||||
"requiredModules": [
|
||||
"refinery-stack"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "ore", "amount": 60 }
|
||||
{
|
||||
"itemId": "ore",
|
||||
"amount": 60
|
||||
}
|
||||
],
|
||||
"cycleTime": 8,
|
||||
"batchSize": 60,
|
||||
@@ -57,9 +65,14 @@
|
||||
"construction": {
|
||||
"recipeId": "hull-fabrication",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requiredModules": [
|
||||
"fabricator-array"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 70 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 70
|
||||
}
|
||||
],
|
||||
"cycleTime": 10,
|
||||
"batchSize": 35,
|
||||
@@ -78,9 +91,14 @@
|
||||
"construction": {
|
||||
"recipeId": "ammo-fabrication",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requiredModules": [
|
||||
"fabricator-array"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 24 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 24
|
||||
}
|
||||
],
|
||||
"cycleTime": 6,
|
||||
"batchSize": 30,
|
||||
@@ -89,27 +107,6 @@
|
||||
"priority": 34
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "naval-guns",
|
||||
"name": "Naval Guns",
|
||||
"description": "Shipboard turret and cannon assemblies.",
|
||||
"type": "component",
|
||||
"cargoKind": "manufactured",
|
||||
"volume": 1.4,
|
||||
"construction": {
|
||||
"recipeId": "gun-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 36 }
|
||||
],
|
||||
"cycleTime": 9,
|
||||
"batchSize": 12,
|
||||
"productsPerHour": 4800,
|
||||
"maxEfficiency": 1,
|
||||
"priority": 32
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ship-equipment",
|
||||
"name": "Ship Equipment",
|
||||
@@ -120,10 +117,18 @@
|
||||
"construction": {
|
||||
"recipeId": "equipment-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requiredModules": [
|
||||
"fabricator-array"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 28 },
|
||||
{ "itemId": "water", "amount": 8 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 28
|
||||
},
|
||||
{
|
||||
"itemId": "water",
|
||||
"amount": 8
|
||||
}
|
||||
],
|
||||
"cycleTime": 11,
|
||||
"batchSize": 18,
|
||||
@@ -142,11 +147,18 @@
|
||||
"construction": {
|
||||
"recipeId": "ship-parts-integration",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requiredModules": [
|
||||
"fabricator-array"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "hull-sections", "amount": 24 },
|
||||
{ "itemId": "naval-guns", "amount": 6 },
|
||||
{ "itemId": "ship-equipment", "amount": 10 }
|
||||
{
|
||||
"itemId": "hull-sections",
|
||||
"amount": 24
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 10
|
||||
}
|
||||
],
|
||||
"cycleTime": 14,
|
||||
"batchSize": 20,
|
||||
@@ -165,10 +177,18 @@
|
||||
"construction": {
|
||||
"recipeId": "drone-parts-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requiredModules": [
|
||||
"fabricator-array"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 12 },
|
||||
{ "itemId": "ship-equipment", "amount": 6 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 12
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 6
|
||||
}
|
||||
],
|
||||
"cycleTime": 7,
|
||||
"batchSize": 16,
|
||||
@@ -187,10 +207,19 @@
|
||||
"construction": {
|
||||
"recipeId": "command-bridge-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 20 },
|
||||
{ "itemId": "ship-equipment", "amount": 10 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 20
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 10
|
||||
}
|
||||
],
|
||||
"cycleTime": 9,
|
||||
"batchSize": 1,
|
||||
@@ -209,10 +238,19 @@
|
||||
"construction": {
|
||||
"recipeId": "reactor-core-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 30 },
|
||||
{ "itemId": "ship-equipment", "amount": 8 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 30
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 8
|
||||
}
|
||||
],
|
||||
"cycleTime": 10,
|
||||
"batchSize": 1,
|
||||
@@ -231,10 +269,19 @@
|
||||
"construction": {
|
||||
"recipeId": "capacitor-bank-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 18 },
|
||||
{ "itemId": "ship-equipment", "amount": 4 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 18
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 4
|
||||
}
|
||||
],
|
||||
"cycleTime": 9,
|
||||
"batchSize": 1,
|
||||
@@ -253,10 +300,19 @@
|
||||
"construction": {
|
||||
"recipeId": "ion-drive-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 22 },
|
||||
{ "itemId": "ship-equipment", "amount": 8 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 22
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 8
|
||||
}
|
||||
],
|
||||
"cycleTime": 10,
|
||||
"batchSize": 1,
|
||||
@@ -275,10 +331,19 @@
|
||||
"construction": {
|
||||
"recipeId": "ftl-core-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 34 },
|
||||
{ "itemId": "ship-equipment", "amount": 14 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 34
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 14
|
||||
}
|
||||
],
|
||||
"cycleTime": 12,
|
||||
"batchSize": 1,
|
||||
@@ -297,10 +362,15 @@
|
||||
"construction": {
|
||||
"recipeId": "gun-turret-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "naval-guns", "amount": 8 },
|
||||
{ "itemId": "refined-metals", "amount": 12 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 12
|
||||
}
|
||||
],
|
||||
"cycleTime": 8,
|
||||
"batchSize": 1,
|
||||
@@ -319,11 +389,23 @@
|
||||
"construction": {
|
||||
"recipeId": "carrier-bay-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "hull-sections", "amount": 18 },
|
||||
{ "itemId": "refined-metals", "amount": 18 },
|
||||
{ "itemId": "ship-equipment", "amount": 10 }
|
||||
{
|
||||
"itemId": "hull-sections",
|
||||
"amount": 18
|
||||
},
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 18
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 10
|
||||
}
|
||||
],
|
||||
"cycleTime": 14,
|
||||
"batchSize": 1,
|
||||
@@ -342,11 +424,23 @@
|
||||
"construction": {
|
||||
"recipeId": "habitat-ring-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "hull-sections", "amount": 14 },
|
||||
{ "itemId": "ship-equipment", "amount": 8 },
|
||||
{ "itemId": "water", "amount": 10 }
|
||||
{
|
||||
"itemId": "hull-sections",
|
||||
"amount": 14
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 8
|
||||
},
|
||||
{
|
||||
"itemId": "water",
|
||||
"amount": 10
|
||||
}
|
||||
],
|
||||
"cycleTime": 12,
|
||||
"batchSize": 1,
|
||||
@@ -365,10 +459,19 @@
|
||||
"construction": {
|
||||
"recipeId": "bulk-bay-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 16 },
|
||||
{ "itemId": "hull-sections", "amount": 10 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 16
|
||||
},
|
||||
{
|
||||
"itemId": "hull-sections",
|
||||
"amount": 10
|
||||
}
|
||||
],
|
||||
"cycleTime": 8,
|
||||
"batchSize": 1,
|
||||
@@ -387,10 +490,19 @@
|
||||
"construction": {
|
||||
"recipeId": "container-bay-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 12 },
|
||||
{ "itemId": "ship-equipment", "amount": 4 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 12
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 4
|
||||
}
|
||||
],
|
||||
"cycleTime": 8,
|
||||
"batchSize": 1,
|
||||
@@ -409,10 +521,19 @@
|
||||
"construction": {
|
||||
"recipeId": "liquid-tank-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 14 },
|
||||
{ "itemId": "ship-equipment", "amount": 4 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 14
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 4
|
||||
}
|
||||
],
|
||||
"cycleTime": 8,
|
||||
"batchSize": 1,
|
||||
@@ -431,10 +552,19 @@
|
||||
"construction": {
|
||||
"recipeId": "mining-turret-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 18 },
|
||||
{ "itemId": "ship-equipment", "amount": 6 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 18
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 6
|
||||
}
|
||||
],
|
||||
"cycleTime": 9,
|
||||
"batchSize": 1,
|
||||
@@ -453,10 +583,19 @@
|
||||
"construction": {
|
||||
"recipeId": "fabricator-array-module-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["component-factory", "container-bay"],
|
||||
"requiredModules": [
|
||||
"component-factory",
|
||||
"container-bay"
|
||||
],
|
||||
"requirements": [
|
||||
{ "itemId": "refined-metals", "amount": 24 },
|
||||
{ "itemId": "ship-equipment", "amount": 10 }
|
||||
{
|
||||
"itemId": "refined-metals",
|
||||
"amount": 24
|
||||
},
|
||||
{
|
||||
"itemId": "ship-equipment",
|
||||
"amount": 10
|
||||
}
|
||||
],
|
||||
"cycleTime": 11,
|
||||
"batchSize": 1,
|
||||
@@ -464,167 +603,5 @@
|
||||
"maxEfficiency": 1,
|
||||
"priority": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "trade-hub-kit",
|
||||
"name": "Trade Hub Kit",
|
||||
"description": "Deployable prefab package for a trade hub station.",
|
||||
"type": "kit",
|
||||
"cargoKind": "manufactured",
|
||||
"volume": 6.0,
|
||||
"construction": {
|
||||
"recipeId": "trade-hub-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requirements": [
|
||||
{ "itemId": "ship-parts", "amount": 26 },
|
||||
{ "itemId": "ship-equipment", "amount": 16 },
|
||||
{ "itemId": "drone-parts", "amount": 10 }
|
||||
],
|
||||
"cycleTime": 18,
|
||||
"batchSize": 1,
|
||||
"productsPerHour": 200,
|
||||
"maxEfficiency": 1,
|
||||
"priority": 24
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "refinery-kit",
|
||||
"name": "Refinery Kit",
|
||||
"description": "Deployable prefab package for a refining station.",
|
||||
"type": "kit",
|
||||
"cargoKind": "manufactured",
|
||||
"volume": 6.5,
|
||||
"construction": {
|
||||
"recipeId": "refinery-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requirements": [
|
||||
{ "itemId": "ship-parts", "amount": 32 },
|
||||
{ "itemId": "hull-sections", "amount": 24 },
|
||||
{ "itemId": "ship-equipment", "amount": 14 }
|
||||
],
|
||||
"cycleTime": 20,
|
||||
"batchSize": 1,
|
||||
"productsPerHour": 180,
|
||||
"maxEfficiency": 1,
|
||||
"priority": 26
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "farm-ring-kit",
|
||||
"name": "Farm Ring Kit",
|
||||
"description": "Deployable prefab package for a farm station.",
|
||||
"type": "kit",
|
||||
"cargoKind": "manufactured",
|
||||
"volume": 6.0,
|
||||
"construction": {
|
||||
"recipeId": "farm-ring-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requirements": [
|
||||
{ "itemId": "ship-parts", "amount": 22 },
|
||||
{ "itemId": "ship-equipment", "amount": 18 },
|
||||
{ "itemId": "water", "amount": 22 }
|
||||
],
|
||||
"cycleTime": 18,
|
||||
"batchSize": 1,
|
||||
"productsPerHour": 200,
|
||||
"maxEfficiency": 1,
|
||||
"priority": 22
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "manufactory-kit",
|
||||
"name": "Manufactory Kit",
|
||||
"description": "Deployable prefab package for an orbital manufactory.",
|
||||
"type": "kit",
|
||||
"cargoKind": "manufactured",
|
||||
"volume": 7.0,
|
||||
"construction": {
|
||||
"recipeId": "manufactory-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requirements": [
|
||||
{ "itemId": "ship-parts", "amount": 34 },
|
||||
{ "itemId": "hull-sections", "amount": 16 },
|
||||
{ "itemId": "ship-equipment", "amount": 18 }
|
||||
],
|
||||
"cycleTime": 22,
|
||||
"batchSize": 1,
|
||||
"productsPerHour": 163.6,
|
||||
"maxEfficiency": 1,
|
||||
"priority": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "shipyard-kit",
|
||||
"name": "Shipyard Kit",
|
||||
"description": "Deployable prefab package for an orbital shipyard.",
|
||||
"type": "kit",
|
||||
"cargoKind": "manufactured",
|
||||
"volume": 8.0,
|
||||
"construction": {
|
||||
"recipeId": "shipyard-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requirements": [
|
||||
{ "itemId": "ship-parts", "amount": 42 },
|
||||
{ "itemId": "hull-sections", "amount": 30 },
|
||||
{ "itemId": "naval-guns", "amount": 10 }
|
||||
],
|
||||
"cycleTime": 26,
|
||||
"batchSize": 1,
|
||||
"productsPerHour": 138.5,
|
||||
"maxEfficiency": 1,
|
||||
"priority": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "defense-grid-kit",
|
||||
"name": "Defense Grid Kit",
|
||||
"description": "Deployable prefab package for a defense platform.",
|
||||
"type": "kit",
|
||||
"cargoKind": "manufactured",
|
||||
"volume": 7.0,
|
||||
"construction": {
|
||||
"recipeId": "defense-grid-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requirements": [
|
||||
{ "itemId": "ship-parts", "amount": 18 },
|
||||
{ "itemId": "naval-guns", "amount": 12 },
|
||||
{ "itemId": "ammo-crates", "amount": 18 }
|
||||
],
|
||||
"cycleTime": 16,
|
||||
"batchSize": 1,
|
||||
"productsPerHour": 225,
|
||||
"maxEfficiency": 1,
|
||||
"priority": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stargate-kit",
|
||||
"name": "Stargate Kit",
|
||||
"description": "Deployable prefab package for a stargate structure.",
|
||||
"type": "kit",
|
||||
"cargoKind": "manufactured",
|
||||
"volume": 10.0,
|
||||
"construction": {
|
||||
"recipeId": "stargate-assembly",
|
||||
"facilityCategory": "station",
|
||||
"requiredModules": ["fabricator-array"],
|
||||
"requirements": [
|
||||
{ "itemId": "ship-parts", "amount": 60 },
|
||||
{ "itemId": "hull-sections", "amount": 44 },
|
||||
{ "itemId": "ship-equipment", "amount": 26 },
|
||||
{ "itemId": "naval-guns", "amount": 8 }
|
||||
],
|
||||
"cycleTime": 34,
|
||||
"batchSize": 1,
|
||||
"productsPerHour": 105.9,
|
||||
"maxEfficiency": 1,
|
||||
"priority": 36
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
{
|
||||
"id": "frigate",
|
||||
"label": "Vanguard Frigate",
|
||||
"role": "military",
|
||||
"shipClass": "frigate",
|
||||
"kind": "military",
|
||||
"class": "frigate",
|
||||
"speed": 120000,
|
||||
"warpSpeed": 0.22,
|
||||
"ftlSpeed": 0.75,
|
||||
@@ -13,13 +13,9 @@
|
||||
"hullColor": "#1f4f78",
|
||||
"size": 4,
|
||||
"maxHealth": 100,
|
||||
"modules": [
|
||||
"command-bridge",
|
||||
"reactor-core",
|
||||
"capacitor-bank",
|
||||
"ion-drive",
|
||||
"ftl-core",
|
||||
"gun-turret"
|
||||
"capabilities": [
|
||||
"warp",
|
||||
"ftl"
|
||||
],
|
||||
"construction": {
|
||||
"recipeId": "frigate-construction",
|
||||
@@ -69,8 +65,8 @@
|
||||
{
|
||||
"id": "destroyer",
|
||||
"label": "Bulwark Destroyer",
|
||||
"role": "military",
|
||||
"shipClass": "destroyer",
|
||||
"kind": "military",
|
||||
"class": "destroyer",
|
||||
"speed": 95000,
|
||||
"warpSpeed": 0.18,
|
||||
"ftlSpeed": 0.68,
|
||||
@@ -80,14 +76,9 @@
|
||||
"hullColor": "#6a2e26",
|
||||
"size": 7,
|
||||
"maxHealth": 240,
|
||||
"modules": [
|
||||
"command-bridge",
|
||||
"reactor-core",
|
||||
"capacitor-bank",
|
||||
"ion-drive",
|
||||
"ftl-core",
|
||||
"gun-turret",
|
||||
"gun-turret"
|
||||
"capabilities": [
|
||||
"warp",
|
||||
"ftl"
|
||||
],
|
||||
"construction": {
|
||||
"recipeId": "destroyer-construction",
|
||||
@@ -137,8 +128,8 @@
|
||||
{
|
||||
"id": "cruiser",
|
||||
"label": "Aegis Cruiser",
|
||||
"role": "military",
|
||||
"shipClass": "cruiser",
|
||||
"kind": "military",
|
||||
"class": "cruiser",
|
||||
"speed": 85000,
|
||||
"warpSpeed": 0.16,
|
||||
"ftlSpeed": 0.62,
|
||||
@@ -148,14 +139,9 @@
|
||||
"hullColor": "#314562",
|
||||
"size": 10,
|
||||
"maxHealth": 340,
|
||||
"modules": [
|
||||
"command-bridge",
|
||||
"reactor-core",
|
||||
"capacitor-bank",
|
||||
"ion-drive",
|
||||
"ftl-core",
|
||||
"gun-turret",
|
||||
"gun-turret"
|
||||
"capabilities": [
|
||||
"warp",
|
||||
"ftl"
|
||||
],
|
||||
"construction": {
|
||||
"recipeId": "cruiser-construction",
|
||||
@@ -205,8 +191,8 @@
|
||||
{
|
||||
"id": "carrier",
|
||||
"label": "Citadel Carrier",
|
||||
"role": "military",
|
||||
"shipClass": "capital",
|
||||
"kind": "military",
|
||||
"class": "capital",
|
||||
"speed": 60000,
|
||||
"warpSpeed": 0.12,
|
||||
"ftlSpeed": 0.5,
|
||||
@@ -216,16 +202,9 @@
|
||||
"hullColor": "#35586d",
|
||||
"size": 16,
|
||||
"maxHealth": 900,
|
||||
"modules": [
|
||||
"command-bridge",
|
||||
"reactor-core",
|
||||
"capacitor-bank",
|
||||
"ion-drive",
|
||||
"ftl-core",
|
||||
"carrier-bay",
|
||||
"carrier-bay",
|
||||
"gun-turret",
|
||||
"habitat-ring"
|
||||
"capabilities": [
|
||||
"warp",
|
||||
"ftl"
|
||||
],
|
||||
"dockingCapacity": 6,
|
||||
"dockingClasses": [
|
||||
@@ -274,10 +253,6 @@
|
||||
{
|
||||
"itemId": "gun-turret-module",
|
||||
"amount": 1
|
||||
},
|
||||
{
|
||||
"itemId": "habitat-ring-module",
|
||||
"amount": 1
|
||||
}
|
||||
],
|
||||
"cycleTime": 60,
|
||||
@@ -289,8 +264,8 @@
|
||||
{
|
||||
"id": "hauler",
|
||||
"label": "Atlas Hauler",
|
||||
"role": "transport",
|
||||
"shipClass": "industrial",
|
||||
"kind": "transport",
|
||||
"class": "industrial",
|
||||
"speed": 70000,
|
||||
"warpSpeed": 0.14,
|
||||
"ftlSpeed": 0.55,
|
||||
@@ -301,13 +276,9 @@
|
||||
"hullColor": "#365f2a",
|
||||
"size": 8,
|
||||
"maxHealth": 180,
|
||||
"modules": [
|
||||
"command-bridge",
|
||||
"reactor-core",
|
||||
"capacitor-bank",
|
||||
"ion-drive",
|
||||
"ftl-core",
|
||||
"container-bay"
|
||||
"capabilities": [
|
||||
"warp",
|
||||
"ftl"
|
||||
],
|
||||
"construction": {
|
||||
"recipeId": "hauler-construction",
|
||||
@@ -357,27 +328,21 @@
|
||||
{
|
||||
"id": "constructor",
|
||||
"label": "Pioneer Constructor",
|
||||
"role": "construction",
|
||||
"shipClass": "industrial",
|
||||
"kind": "construction",
|
||||
"class": "industrial",
|
||||
"speed": 65000,
|
||||
"warpSpeed": 0.13,
|
||||
"ftlSpeed": 0.48,
|
||||
"spoolTime": 3.5,
|
||||
"cargoCapacity": 160,
|
||||
"cargoKind": "manufactured",
|
||||
"cargoItemId": "drone-parts",
|
||||
"color": "#9af0c1",
|
||||
"hullColor": "#2d5d47",
|
||||
"size": 9,
|
||||
"maxHealth": 220,
|
||||
"modules": [
|
||||
"command-bridge",
|
||||
"reactor-core",
|
||||
"capacitor-bank",
|
||||
"ion-drive",
|
||||
"ftl-core",
|
||||
"fabricator-array",
|
||||
"container-bay"
|
||||
"capabilities": [
|
||||
"warp",
|
||||
"ftl"
|
||||
],
|
||||
"construction": {
|
||||
"recipeId": "constructor-construction",
|
||||
@@ -431,27 +396,22 @@
|
||||
{
|
||||
"id": "miner",
|
||||
"label": "Prospector Miner",
|
||||
"role": "mining",
|
||||
"shipClass": "industrial",
|
||||
"kind": "mining",
|
||||
"class": "industrial",
|
||||
"speed": 75000,
|
||||
"warpSpeed": 0.15,
|
||||
"ftlSpeed": 0.5,
|
||||
"spoolTime": 3.1,
|
||||
"cargoCapacity": 120,
|
||||
"cargoKind": "bulk-solid",
|
||||
"cargoItemId": "ore",
|
||||
"cargoKind": "solid",
|
||||
"color": "#ffdd75",
|
||||
"hullColor": "#68552b",
|
||||
"size": 6,
|
||||
"maxHealth": 150,
|
||||
"modules": [
|
||||
"command-bridge",
|
||||
"reactor-core",
|
||||
"capacitor-bank",
|
||||
"ion-drive",
|
||||
"ftl-core",
|
||||
"mining-turret",
|
||||
"bulk-bay"
|
||||
"capabilities": [
|
||||
"warp",
|
||||
"ftl",
|
||||
"mining"
|
||||
],
|
||||
"construction": {
|
||||
"recipeId": "miner-construction",
|
||||
|
||||
Reference in New Issue
Block a user