590 lines
20 KiB
C#
590 lines
20 KiB
C#
using static SpaceGame.Api.Stations.Simulation.InfrastructureSimulationService;
|
|
using static SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport;
|
|
|
|
namespace SpaceGame.Api.Ships.Simulation;
|
|
|
|
internal sealed partial class ShipTaskExecutionService
|
|
{
|
|
private static bool AdvanceTimedAction(ShipRuntime ship, float deltaSeconds, float requiredSeconds)
|
|
{
|
|
ship.ActionTimer += deltaSeconds;
|
|
if (ship.ActionTimer < requiredSeconds)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ship.ActionTimer = 0f;
|
|
return true;
|
|
}
|
|
|
|
private static void BeginTrackedAction(ShipRuntime ship, string actionKey, float total)
|
|
{
|
|
if (ship.TrackedActionKey == actionKey)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ship.TrackedActionKey = actionKey;
|
|
ship.TrackedActionTotal = MathF.Max(total, 0.01f);
|
|
}
|
|
|
|
internal static float GetShipCargoAmount(ShipRuntime ship) =>
|
|
SimulationRuntimeSupport.GetShipCargoAmount(ship);
|
|
|
|
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, world))
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var cargoAmount = GetShipCargoAmount(ship);
|
|
if (cargoAmount >= ship.Definition.CargoCapacity - 0.01f)
|
|
{
|
|
ship.ActionTimer = 0f;
|
|
ship.State = ShipState.CargoFull;
|
|
ship.TargetPosition = ship.Position;
|
|
return "cargo-full";
|
|
}
|
|
|
|
ship.TargetPosition = task.TargetPosition.Value;
|
|
var distance = ship.Position.DistanceTo(task.TargetPosition.Value);
|
|
if (distance > task.Threshold)
|
|
{
|
|
ship.ActionTimer = 0f;
|
|
|
|
ship.State = ShipState.MiningApproach;
|
|
ship.Position = ship.Position.MoveToward(task.TargetPosition.Value, GetLocalTravelSpeed(ship) * deltaSeconds);
|
|
return "none";
|
|
}
|
|
|
|
ship.State = ShipState.Mining;
|
|
if (!AdvanceTimedAction(ship, deltaSeconds, world.Balance.MiningCycleSeconds))
|
|
{
|
|
return "none";
|
|
}
|
|
|
|
var remainingCapacity = MathF.Max(0f, ship.Definition.CargoCapacity - cargoAmount);
|
|
var mined = MathF.Min(world.Balance.MiningRate, remainingCapacity);
|
|
mined = MathF.Min(mined, node.OreRemaining);
|
|
if (mined <= 0.01f)
|
|
{
|
|
ship.ActionTimer = 0f;
|
|
ship.State = node.OreRemaining <= 0.01f ? ShipState.NodeDepleted : ShipState.CargoFull;
|
|
ship.TargetPosition = ship.Position;
|
|
return node.OreRemaining <= 0.01f ? "node-depleted" : "cargo-full";
|
|
}
|
|
|
|
AddInventory(ship.Inventory, node.ItemId, mined);
|
|
|
|
node.OreRemaining -= mined;
|
|
node.OreRemaining = MathF.Max(0f, node.OreRemaining);
|
|
|
|
return GetShipCargoAmount(ship) >= ship.Definition.CargoCapacity ? "cargo-full" : "none";
|
|
}
|
|
|
|
private string UpdateDock(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
|
{
|
|
var task = ship.ControllerTask;
|
|
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == task.TargetEntityId);
|
|
if (station is null || task.TargetPosition is null)
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var padIndex = ship.AssignedDockingPadIndex ?? ReserveDockingPad(station, ship.Id);
|
|
if (padIndex is null)
|
|
{
|
|
ship.ActionTimer = 0f;
|
|
ship.State = ShipState.AwaitingDock;
|
|
ship.TargetPosition = GetDockingHoldPosition(station, ship.Id);
|
|
var waitDistance = ship.Position.DistanceTo(ship.TargetPosition);
|
|
if (waitDistance > 4f)
|
|
{
|
|
ship.Position = ship.Position.MoveToward(ship.TargetPosition, GetLocalTravelSpeed(ship) * deltaSeconds);
|
|
}
|
|
|
|
return "none";
|
|
}
|
|
|
|
ship.AssignedDockingPadIndex = padIndex;
|
|
var padPosition = GetDockingPadPosition(station, padIndex.Value);
|
|
ship.TargetPosition = padPosition;
|
|
var distance = ship.Position.DistanceTo(padPosition);
|
|
if (distance > 4f)
|
|
{
|
|
ship.ActionTimer = 0f;
|
|
|
|
ship.State = ShipState.DockingApproach;
|
|
ship.Position = ship.Position.MoveToward(padPosition, GetLocalTravelSpeed(ship) * deltaSeconds);
|
|
return "none";
|
|
}
|
|
|
|
ship.State = ShipState.Docking;
|
|
if (!AdvanceTimedAction(ship, deltaSeconds, world.Balance.DockingDuration))
|
|
{
|
|
return "none";
|
|
}
|
|
|
|
ship.State = ShipState.Docked;
|
|
ship.DockedStationId = station.Id;
|
|
station.DockedShipIds.Add(ship.Id);
|
|
ship.Position = padPosition;
|
|
ship.TargetPosition = padPosition;
|
|
return "docked";
|
|
}
|
|
|
|
private string UpdateUnload(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
|
{
|
|
if (ship.DockedStationId is null)
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DockedStationId);
|
|
if (station is null)
|
|
{
|
|
ship.DockedStationId = null;
|
|
ship.AssignedDockingPadIndex = null;
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
ship.TargetPosition = GetShipDockedPosition(ship, station);
|
|
ship.Position = ship.TargetPosition;
|
|
ship.ActionTimer = 0f;
|
|
ship.State = ShipState.Transferring;
|
|
BeginTrackedAction(ship, "transferring", GetShipCargoAmount(ship));
|
|
|
|
var faction = world.Factions.FirstOrDefault(candidate => candidate.Id == ship.FactionId);
|
|
var transferredAny = false;
|
|
foreach (var (itemId, amount) in ship.Inventory.ToList())
|
|
{
|
|
var moved = MathF.Min(amount, world.Balance.TransferRate * deltaSeconds);
|
|
var accepted = TryAddStationInventory(world, station, itemId, moved);
|
|
transferredAny |= accepted > 0.01f;
|
|
RemoveInventory(ship.Inventory, itemId, accepted);
|
|
if (faction is not null && string.Equals(itemId, "ore", StringComparison.Ordinal))
|
|
{
|
|
faction.OreMined += accepted;
|
|
faction.Credits += accepted * 0.4f;
|
|
}
|
|
}
|
|
|
|
if (!transferredAny && GetShipCargoAmount(ship) > 0.01f && HasShipCapabilities(ship.Definition, "mining"))
|
|
{
|
|
ship.Inventory.Clear();
|
|
return "unloaded";
|
|
}
|
|
|
|
return GetShipCargoAmount(ship) <= 0.01f ? "unloaded" : "none";
|
|
}
|
|
|
|
private string UpdateLoadCargo(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
|
{
|
|
if (ship.DockedStationId is null)
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DockedStationId);
|
|
if (station is null)
|
|
{
|
|
ship.DockedStationId = null;
|
|
ship.AssignedDockingPadIndex = null;
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
ship.TargetPosition = GetShipDockedPosition(ship, station);
|
|
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 transfer = MathF.Min(world.Balance.TransferRate * deltaSeconds, ship.Definition.CargoCapacity - GetShipCargoAmount(ship));
|
|
var moved = itemId is null ? 0f : MathF.Min(transfer, GetInventoryAmount(station.Inventory, itemId));
|
|
if (itemId is not null && moved > 0.01f)
|
|
{
|
|
RemoveInventory(station.Inventory, itemId, moved);
|
|
AddInventory(ship.Inventory, itemId, moved);
|
|
}
|
|
|
|
return itemId is null
|
|
|| GetShipCargoAmount(ship) >= ship.Definition.CargoCapacity - 0.01f
|
|
|| GetInventoryAmount(station.Inventory, itemId) <= 0.01f
|
|
? "loaded"
|
|
: "none";
|
|
}
|
|
|
|
private string UpdateConstructModule(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
|
{
|
|
var station = ResolveShipSupportStation(ship, world);
|
|
if (station is null || ship.DefaultBehavior.ModuleId is null)
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
if (!world.ModuleRecipes.TryGetValue(ship.DefaultBehavior.ModuleId, out var recipe))
|
|
{
|
|
ship.AssignedDockingPadIndex = null;
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var supportPosition = ResolveShipSupportPosition(ship, station, null, world);
|
|
if (!IsShipWithinSupportRange(ship, supportPosition, ship.ControllerTask.Threshold))
|
|
{
|
|
ship.State = ShipState.LocalFlight;
|
|
ship.TargetPosition = supportPosition;
|
|
ship.Position = ship.Position.MoveToward(supportPosition, GetLocalTravelSpeed(ship) * deltaSeconds);
|
|
return "none";
|
|
}
|
|
|
|
if (!TryEnsureModuleConstructionStarted(station, recipe, ship.Id))
|
|
{
|
|
ship.ActionTimer = 0f;
|
|
ship.State = ShipState.WaitingMaterials;
|
|
ship.TargetPosition = supportPosition;
|
|
return "none";
|
|
}
|
|
|
|
if (station.ActiveConstruction?.AssignedConstructorShipId != ship.Id)
|
|
{
|
|
ship.State = ShipState.ConstructionBlocked;
|
|
ship.TargetPosition = supportPosition;
|
|
return "none";
|
|
}
|
|
|
|
ship.TargetPosition = supportPosition;
|
|
ship.Position = ship.TargetPosition;
|
|
ship.ActionTimer = 0f;
|
|
ship.State = ShipState.Constructing;
|
|
station.ActiveConstruction.ProgressSeconds += deltaSeconds;
|
|
if (station.ActiveConstruction.ProgressSeconds < station.ActiveConstruction.RequiredSeconds)
|
|
{
|
|
return "none";
|
|
}
|
|
|
|
AddStationModule(world, station, station.ActiveConstruction.ModuleId);
|
|
station.ActiveConstruction = null;
|
|
return "module-constructed";
|
|
}
|
|
|
|
private string UpdateDeliverConstruction(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
|
{
|
|
var station = ResolveShipSupportStation(ship, world);
|
|
if (station is null)
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var site = world.ConstructionSites.FirstOrDefault(candidate => candidate.Id == ship.ControllerTask.TargetEntityId);
|
|
if (station is null || site is null || site.State != ConstructionSiteStateKinds.Active)
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var supportPosition = ResolveShipSupportPosition(ship, station, site, world);
|
|
if (!IsShipWithinSupportRange(ship, supportPosition, ship.ControllerTask.Threshold))
|
|
{
|
|
ship.State = ShipState.LocalFlight;
|
|
ship.TargetPosition = supportPosition;
|
|
ship.Position = ship.Position.MoveToward(supportPosition, GetLocalTravelSpeed(ship) * deltaSeconds);
|
|
return "none";
|
|
}
|
|
|
|
ship.TargetPosition = supportPosition;
|
|
ship.Position = ship.TargetPosition;
|
|
ship.ActionTimer = 0f;
|
|
ship.State = ShipState.DeliveringConstruction;
|
|
BeginTrackedAction(ship, "delivering-construction", GetRemainingConstructionDelivery(world, site));
|
|
|
|
if (site.StationId is not null)
|
|
{
|
|
foreach (var required in site.RequiredItems)
|
|
{
|
|
var delivered = GetInventoryAmount(site.DeliveredItems, required.Key);
|
|
var remaining = MathF.Max(0f, required.Value - delivered);
|
|
if (remaining <= 0.01f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var moved = MathF.Min(remaining, world.Balance.TransferRate * deltaSeconds);
|
|
moved = MathF.Min(moved, GetInventoryAmount(station.Inventory, required.Key));
|
|
if (moved <= 0.01f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RemoveInventory(station.Inventory, required.Key, moved);
|
|
AddInventory(site.Inventory, required.Key, moved);
|
|
AddInventory(site.DeliveredItems, required.Key, moved);
|
|
return IsConstructionSiteReady(world, site) ? "construction-delivered" : "none";
|
|
}
|
|
|
|
return IsConstructionSiteReady(world, site) ? "construction-delivered" : "none";
|
|
}
|
|
|
|
foreach (var required in site.RequiredItems)
|
|
{
|
|
var delivered = GetInventoryAmount(site.DeliveredItems, required.Key);
|
|
var remaining = MathF.Max(0f, required.Value - delivered);
|
|
if (remaining <= 0.01f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var moved = MathF.Min(remaining, world.Balance.TransferRate * deltaSeconds);
|
|
moved = MathF.Min(moved, GetInventoryAmount(station.Inventory, required.Key));
|
|
if (moved <= 0.01f)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
RemoveInventory(station.Inventory, required.Key, moved);
|
|
AddInventory(site.Inventory, required.Key, moved);
|
|
AddInventory(site.DeliveredItems, required.Key, moved);
|
|
return IsConstructionSiteReady(world, site) ? "construction-delivered" : "none";
|
|
}
|
|
|
|
return IsConstructionSiteReady(world, site) ? "construction-delivered" : "none";
|
|
}
|
|
|
|
private string UpdateBuildConstructionSite(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
|
{
|
|
var station = ResolveShipSupportStation(ship, world);
|
|
if (station is null)
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var site = world.ConstructionSites.FirstOrDefault(candidate => candidate.Id == ship.ControllerTask.TargetEntityId);
|
|
if (station is null || site is null || site.BlueprintId is null || site.State != ConstructionSiteStateKinds.Active)
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var supportPosition = ResolveShipSupportPosition(ship, station, site, world);
|
|
if (!IsShipWithinSupportRange(ship, supportPosition, ship.ControllerTask.Threshold))
|
|
{
|
|
ship.State = ShipState.LocalFlight;
|
|
ship.TargetPosition = supportPosition;
|
|
ship.Position = ship.Position.MoveToward(supportPosition, GetLocalTravelSpeed(ship) * deltaSeconds);
|
|
return "none";
|
|
}
|
|
|
|
if (!IsConstructionSiteReady(world, site) || !world.ModuleRecipes.TryGetValue(site.BlueprintId, out var recipe))
|
|
{
|
|
ship.State = ShipState.WaitingMaterials;
|
|
ship.TargetPosition = supportPosition;
|
|
return "none";
|
|
}
|
|
|
|
ship.TargetPosition = supportPosition;
|
|
ship.Position = ship.TargetPosition;
|
|
ship.ActionTimer = 0f;
|
|
ship.State = ShipState.Constructing;
|
|
site.AssignedConstructorShipIds.Add(ship.Id);
|
|
site.Progress += deltaSeconds;
|
|
if (site.Progress < recipe.Duration)
|
|
{
|
|
return "none";
|
|
}
|
|
|
|
if (site.StationId is null)
|
|
{
|
|
CompleteStationFoundation(world, station, site);
|
|
}
|
|
else
|
|
{
|
|
AddStationModule(world, station, site.BlueprintId);
|
|
PrepareNextConstructionSiteStep(world, station, site);
|
|
}
|
|
|
|
return "site-constructed";
|
|
}
|
|
|
|
private StationRuntime? ResolveShipSupportStation(ShipRuntime ship, SimulationWorld world) =>
|
|
ship.DockedStationId is not null
|
|
? world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DockedStationId)
|
|
: ship.DefaultBehavior.Kind == "construct-station" && ship.DefaultBehavior.StationId is not null
|
|
? world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DefaultBehavior.StationId)
|
|
: null;
|
|
|
|
private static Vector3 ResolveShipSupportPosition(ShipRuntime ship, StationRuntime station, ConstructionSiteRuntime? site, SimulationWorld world)
|
|
{
|
|
if (ship.DockedStationId is not null)
|
|
{
|
|
return GetShipDockedPosition(ship, station);
|
|
}
|
|
|
|
if (site?.StationId is null && site is not null)
|
|
{
|
|
var anchorPosition = world.Celestials.FirstOrDefault(candidate => candidate.Id == site.CelestialId)?.Position ?? station.Position;
|
|
return GetResourceHoldPosition(anchorPosition, ship.Id, 78f);
|
|
}
|
|
|
|
return GetConstructionHoldPosition(station, ship.Id);
|
|
}
|
|
|
|
private static bool IsShipWithinSupportRange(ShipRuntime ship, Vector3 supportPosition, float threshold) =>
|
|
ship.Position.DistanceTo(supportPosition) <= MathF.Max(threshold, 6f);
|
|
|
|
|
|
private string UpdateUndock(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
|
|
{
|
|
var task = ship.ControllerTask;
|
|
if (ship.DockedStationId is null || task.TargetPosition is null)
|
|
{
|
|
ship.State = ShipState.Idle;
|
|
ship.TargetPosition = ship.Position;
|
|
return "none";
|
|
}
|
|
|
|
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DockedStationId);
|
|
var undockTarget = station is null
|
|
? task.TargetPosition.Value
|
|
: GetUndockTargetPosition(station, ship.AssignedDockingPadIndex, world.Balance.UndockDistance);
|
|
ship.TargetPosition = undockTarget;
|
|
|
|
ship.State = ShipState.Undocking;
|
|
if (!AdvanceTimedAction(ship, deltaSeconds, world.Balance.UndockingDuration))
|
|
{
|
|
if (station is not null)
|
|
{
|
|
ship.Position = GetShipDockedPosition(ship, station);
|
|
}
|
|
|
|
return "none";
|
|
}
|
|
|
|
ship.Position = ship.Position.MoveToward(undockTarget, world.Balance.UndockDistance);
|
|
if (ship.Position.DistanceTo(undockTarget) > task.Threshold)
|
|
{
|
|
return "none";
|
|
}
|
|
|
|
if (station is not null)
|
|
{
|
|
station.DockedShipIds.Remove(ship.Id);
|
|
ReleaseDockingPad(station, ship.Id);
|
|
}
|
|
|
|
ship.DockedStationId = null;
|
|
ship.AssignedDockingPadIndex = null;
|
|
return "undocked";
|
|
}
|
|
|
|
internal static float GetRemainingConstructionDelivery(SimulationWorld world, ConstructionSiteRuntime site) =>
|
|
site.RequiredItems.Sum(required => MathF.Max(0f, required.Value - GetConstructionDeliveredAmount(world, site, required.Key)));
|
|
|
|
private static void CompleteStationFoundation(SimulationWorld world, StationRuntime supportStation, ConstructionSiteRuntime site)
|
|
{
|
|
var anchor = world.Celestials.FirstOrDefault(candidate => candidate.Id == site.CelestialId);
|
|
if (anchor is null || site.BlueprintId is null)
|
|
{
|
|
site.State = ConstructionSiteStateKinds.Destroyed;
|
|
return;
|
|
}
|
|
|
|
var station = new StationRuntime
|
|
{
|
|
Id = $"station-{world.Stations.Count + 1}",
|
|
SystemId = site.SystemId,
|
|
Label = BuildFoundedStationLabel(site.TargetDefinitionId),
|
|
Category = "station",
|
|
Objective = DetermineFoundationObjective(site.TargetDefinitionId),
|
|
Color = world.Factions.FirstOrDefault(candidate => candidate.Id == site.FactionId)?.Color ?? supportStation.Color,
|
|
Position = anchor.Position,
|
|
FactionId = site.FactionId,
|
|
CelestialId = site.CelestialId,
|
|
Health = 600f,
|
|
MaxHealth = 600f,
|
|
};
|
|
|
|
foreach (var moduleId in GetFoundationModules(world, site.BlueprintId))
|
|
{
|
|
AddStationModule(world, station, moduleId);
|
|
}
|
|
|
|
world.Stations.Add(station);
|
|
StationLifecycleService.EnsureStationCommander(world, station);
|
|
anchor.OccupyingStructureId = station.Id;
|
|
site.StationId = station.Id;
|
|
PrepareNextConstructionSiteStep(world, station, site);
|
|
}
|
|
|
|
private static IReadOnlyList<string> GetFoundationModules(SimulationWorld world, string primaryModuleId)
|
|
{
|
|
var modules = new List<string> { "module_arg_dock_m_01_lowtech" };
|
|
foreach (var itemId in world.ProductionGraph.OutputsByModuleId.GetValueOrDefault(primaryModuleId, []))
|
|
{
|
|
if (world.ItemDefinitions.TryGetValue(itemId, out var itemDefinition))
|
|
{
|
|
var storageModule = GetStorageRequirement(itemDefinition.CargoKind);
|
|
if (storageModule is not null && !modules.Contains(storageModule, StringComparer.Ordinal))
|
|
{
|
|
modules.Add(storageModule);
|
|
}
|
|
else if (storageModule is null && !modules.Contains("module_arg_stor_container_m_01", StringComparer.Ordinal))
|
|
{
|
|
modules.Add("module_arg_stor_container_m_01");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!modules.Contains("module_arg_stor_container_m_01", StringComparer.Ordinal))
|
|
{
|
|
modules.Add("module_arg_stor_container_m_01");
|
|
}
|
|
|
|
if (!string.Equals(primaryModuleId, "module_gen_prod_energycells_01", StringComparison.Ordinal))
|
|
{
|
|
modules.Add("module_gen_prod_energycells_01");
|
|
}
|
|
|
|
modules.Add(primaryModuleId);
|
|
return modules.Distinct(StringComparer.Ordinal).ToList();
|
|
}
|
|
|
|
private static string DetermineFoundationObjective(string commodityId) =>
|
|
commodityId switch
|
|
{
|
|
"energycells" => "power",
|
|
"water" => "water",
|
|
"refinedmetals" => "refinery",
|
|
"hullparts" => "hullparts",
|
|
"claytronics" => "claytronics",
|
|
"shipyard" => "shipyard",
|
|
_ => "general",
|
|
};
|
|
|
|
private static string BuildFoundedStationLabel(string commodityId) =>
|
|
$"{char.ToUpperInvariant(commodityId[0])}{commodityId[1..]} Foundry";
|
|
}
|