diff --git a/apps/backend/Contracts/WorldContracts.Ships.cs b/apps/backend/Contracts/WorldContracts.Ships.cs index e213de9..2c35cf5 100644 --- a/apps/backend/Contracts/WorldContracts.Ships.cs +++ b/apps/backend/Contracts/WorldContracts.Ships.cs @@ -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 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 Inventory, diff --git a/apps/backend/Data/WorldDefinitions.cs b/apps/backend/Data/WorldDefinitions.cs index 33cb9fb..aba7ab6 100644 --- a/apps/backend/Data/WorldDefinitions.cs +++ b/apps/backend/Data/WorldDefinitions.cs @@ -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 Modules { get; set; } = []; + public List Capabilities { get; set; } = []; public ConstructionDefinition? Construction { get; set; } } diff --git a/apps/backend/Simulation/AI/ShipBehaviorStateMachine.cs b/apps/backend/Simulation/AI/ShipBehaviorStateMachine.cs index 5ec5c38..8bf6207 100644 --- a/apps/backend/Simulation/AI/ShipBehaviorStateMachine.cs +++ b/apps/backend/Simulation/AI/ShipBehaviorStateMachine.cs @@ -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(), }; diff --git a/apps/backend/Simulation/Model/ShipRuntimeModels.cs b/apps/backend/Simulation/Model/ShipRuntimeModels.cs index a389f39..a5a7724 100644 --- a/apps/backend/Simulation/Model/ShipRuntimeModels.cs +++ b/apps/backend/Simulation/Model/ShipRuntimeModels.cs @@ -18,7 +18,7 @@ public sealed class ShipRuntime public required ControllerTaskRuntime ControllerTask { get; set; } public float ActionTimer { get; set; } public Dictionary 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; } } diff --git a/apps/backend/Simulation/Model/SimulationKinds.cs b/apps/backend/Simulation/Model/SimulationKinds.cs index 2b4561a..9553307 100644 --- a/apps/backend/Simulation/Model/SimulationKinds.cs +++ b/apps/backend/Simulation/Model/SimulationKinds.cs @@ -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), diff --git a/apps/backend/Simulation/ScenarioLoader.Seeding.cs b/apps/backend/Simulation/ScenarioLoader.Seeding.cs index 974aeab..f9385b1 100644 --- a/apps/backend/Simulation/ScenarioLoader.Seeding.cs +++ b/apps/backend/Simulation/ScenarioLoader.Seeding.cs @@ -379,7 +379,7 @@ public sealed partial class ScenarioLoader IReadOnlyDictionary> 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 { diff --git a/apps/backend/Simulation/ScenarioLoader.cs b/apps/backend/Simulation/ScenarioLoader.cs index 1477e85..b53c1ab 100644 --- a/apps/backend/Simulation/ScenarioLoader.cs +++ b/apps/backend/Simulation/ScenarioLoader.cs @@ -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 moduleDefinitions, string moduleId) { diff --git a/apps/backend/Simulation/SimulationEngine.MovementSystem.cs b/apps/backend/Simulation/SimulationEngine.MovementSystem.cs index b9bd9d4..bb0125a 100644 --- a/apps/backend/Simulation/SimulationEngine.MovementSystem.cs +++ b/apps/backend/Simulation/SimulationEngine.MovementSystem.cs @@ -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); } diff --git a/apps/backend/Simulation/SimulationEngine.PowerAndInventorySystems.cs b/apps/backend/Simulation/SimulationEngine.PowerAndInventorySystems.cs index 0ecf7d0..c317d79 100644 --- a/apps/backend/Simulation/SimulationEngine.PowerAndInventorySystems.cs +++ b/apps/backend/Simulation/SimulationEngine.PowerAndInventorySystems.cs @@ -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, }; diff --git a/apps/backend/Simulation/SimulationEngine.Replication.cs b/apps/backend/Simulation/SimulationEngine.Replication.cs index 699f603..c37f6ba 100644 --- a/apps/backend/Simulation/SimulationEngine.Replication.cs +++ b/apps/backend/Simulation/SimulationEngine.Replication.cs @@ -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 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 diff --git a/apps/backend/Simulation/SimulationEngine.ResourceAndInfrastructureSystems.cs b/apps/backend/Simulation/SimulationEngine.ResourceAndInfrastructureSystems.cs index 3f46807..abe91d8 100644 --- a/apps/backend/Simulation/SimulationEngine.ResourceAndInfrastructureSystems.cs +++ b/apps/backend/Simulation/SimulationEngine.ResourceAndInfrastructureSystems.cs @@ -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), diff --git a/apps/backend/Simulation/SimulationEngine.ShipActionSystem.cs b/apps/backend/Simulation/SimulationEngine.ShipActionSystem.cs index d4b143b..3c3c024 100644 --- a/apps/backend/Simulation/SimulationEngine.ShipActionSystem.cs +++ b/apps/backend/Simulation/SimulationEngine.ShipActionSystem.cs @@ -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) { diff --git a/apps/backend/Simulation/SimulationEngine.ShipControl.cs b/apps/backend/Simulation/SimulationEngine.ShipControl.cs index 9dfee6d..b00a1ee 100644 --- a/apps/backend/Simulation/SimulationEngine.ShipControl.cs +++ b/apps/backend/Simulation/SimulationEngine.ShipControl.cs @@ -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, diff --git a/apps/backend/Simulation/SimulationEngine.StationSystems.cs b/apps/backend/Simulation/SimulationEngine.StationSystems.cs index 15d7f38..7a983dd 100644 --- a/apps/backend/Simulation/SimulationEngine.StationSystems.cs +++ b/apps/backend/Simulation/SimulationEngine.StationSystems.cs @@ -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 { diff --git a/apps/viewer/src/contractsShips.ts b/apps/viewer/src/contractsShips.ts index 35eaa35..b7c5d7d 100644 --- a/apps/viewer/src/contractsShips.ts +++ b/apps/viewer/src/contractsShips.ts @@ -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[]; diff --git a/apps/viewer/src/viewerFactionStrip.ts b/apps/viewer/src/viewerFactionStrip.ts index ef6fb02..4d6fa65 100644 --- a/apps/viewer/src/viewerFactionStrip.ts +++ b/apps/viewer/src/viewerFactionStrip.ts @@ -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(

${ship.label}

- ${ship.shipClass} + ${ship.class}
` : ""} -

Cargo ${cargoLabel} ${cargoUsed.toFixed(0)} / ${ship.cargoCapacity.toFixed(0)}

+

Cargo ${cargoUsed.toFixed(0)} / ${ship.cargoCapacity.toFixed(0)}

Inventory ${formatInventory(ship.inventory)}

Speed ${formatShipSpeed(ship)}

Camera ${cameraMode === "follow" && cameraTargetShipId === ship.id ? "camera-follow" : "tactical"}
Press C to toggle follow

diff --git a/apps/viewer/src/viewerSceneAppearance.ts b/apps/viewer/src/viewerSceneAppearance.ts index 2d170ca..e710e97 100644 --- a/apps/viewer/src/viewerSceneAppearance.ts +++ b/apps/viewer/src/viewerSceneAppearance.ts @@ -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) { diff --git a/shared/data/items.json b/shared/data/items.json index 79e84de..a15a0ce 100644 --- a/shared/data/items.json +++ b/shared/data/items.json @@ -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 - } } ] diff --git a/shared/data/ships.json b/shared/data/ships.json index 8f41233..f8cccd4 100644 --- a/shared/data/ships.json +++ b/shared/data/ships.json @@ -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",