Refactor station modules into typed runtime models
This commit is contained in:
@@ -115,6 +115,14 @@ public enum ModuleType
|
||||
Storage,
|
||||
}
|
||||
|
||||
public enum StorageKind
|
||||
{
|
||||
Condensate,
|
||||
Container,
|
||||
Liquid,
|
||||
Solid,
|
||||
}
|
||||
|
||||
public static class CommanderKind
|
||||
{
|
||||
public const string Faction = "faction";
|
||||
@@ -209,20 +217,54 @@ public static class SimulationEnumMappings
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(moduleType), moduleType, null),
|
||||
};
|
||||
|
||||
public static ModuleType ToModuleType(this string value) => value.Trim() switch
|
||||
public static ModuleType ToModuleType(this string value)
|
||||
{
|
||||
"buildmodule" => ModuleType.BuildModule,
|
||||
"connectionmodule" => ModuleType.ConnectionModule,
|
||||
"defencemodule" => ModuleType.DefenceModule,
|
||||
"dockarea" => ModuleType.DockArea,
|
||||
"habitation" => ModuleType.Habitation,
|
||||
"pier" => ModuleType.Pier,
|
||||
"processingmodule" => ModuleType.ProcessingModule,
|
||||
"production" => ModuleType.Production,
|
||||
"storage" => ModuleType.Storage,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unsupported module type."),
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, "Module type is required.");
|
||||
}
|
||||
|
||||
return value.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"buildmodule" => ModuleType.BuildModule,
|
||||
"connectionmodule" => ModuleType.ConnectionModule,
|
||||
"defencemodule" => ModuleType.DefenceModule,
|
||||
"dockarea" => ModuleType.DockArea,
|
||||
"habitation" => ModuleType.Habitation,
|
||||
"pier" => ModuleType.Pier,
|
||||
"processingmodule" => ModuleType.ProcessingModule,
|
||||
"production" => ModuleType.Production,
|
||||
"storage" => ModuleType.Storage,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unsupported module type."),
|
||||
};
|
||||
}
|
||||
|
||||
public static string ToDataValue(this StorageKind storageKind) => storageKind switch
|
||||
{
|
||||
StorageKind.Condensate => "condensate",
|
||||
StorageKind.Container => "container",
|
||||
StorageKind.Liquid => "liquid",
|
||||
StorageKind.Solid => "solid",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(storageKind), storageKind, null),
|
||||
};
|
||||
|
||||
public static StorageKind ToStorageKind(this string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, "Storage kind is required.");
|
||||
}
|
||||
|
||||
return value.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"condensate" => StorageKind.Condensate,
|
||||
"container" => StorageKind.Container,
|
||||
"liquid" => StorageKind.Liquid,
|
||||
"solid" => StorageKind.Solid,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unsupported storage kind."),
|
||||
};
|
||||
}
|
||||
|
||||
public static string ToContractValue(this SpatialNodeKind kind) => kind switch
|
||||
{
|
||||
SpatialNodeKind.Star => "star",
|
||||
|
||||
@@ -6,13 +6,55 @@ internal static class SimulationRuntimeSupport
|
||||
internal static bool HasShipCapabilities(ShipDefinition definition, params string[] capabilities) =>
|
||||
capabilities.All(cap => definition.Capabilities.Contains(cap, StringComparer.Ordinal));
|
||||
|
||||
internal static int CountStationModules(StationRuntime station, string moduleId) =>
|
||||
station.Modules.Count(module => string.Equals(module.ModuleId, moduleId, StringComparison.Ordinal));
|
||||
internal static int CountStationModules(StationRuntime station, ModuleType moduleType) =>
|
||||
station.Modules.Count(module => module.ModuleType == moduleType);
|
||||
|
||||
internal static int CountStationModules(SimulationWorld world, StationRuntime station, ModuleType moduleType) =>
|
||||
station.Modules.Count(module =>
|
||||
world.ModuleDefinitions.TryGetValue(module.ModuleId, out var definition)
|
||||
&& definition.ModuleType == moduleType);
|
||||
internal static float GetStationStorageCapacity(SimulationWorld world, StationRuntime station, StorageKind storageKind)
|
||||
{
|
||||
SyncStorageModuleLevels(world, station, storageKind);
|
||||
return GetStorageModules(world, station, storageKind)
|
||||
.Sum(entry => entry.Definition.StorageCapacity);
|
||||
}
|
||||
|
||||
internal static bool HasStorageCapacity(SimulationWorld world, StationRuntime station, StorageKind storageKind)
|
||||
{
|
||||
SyncStorageModuleLevels(world, station, storageKind);
|
||||
return GetStorageModules(world, station, storageKind).Any();
|
||||
}
|
||||
|
||||
private static IEnumerable<(StorageStationModuleRuntime Module, StorageModuleDefinition Definition)> GetStorageModules(
|
||||
SimulationWorld world,
|
||||
StationRuntime station,
|
||||
StorageKind storageKind) =>
|
||||
station.Modules
|
||||
.OfType<StorageStationModuleRuntime>()
|
||||
.Where(module => module.StorageKind == storageKind)
|
||||
.Select(module => world.ModuleDefinitions.TryGetValue(module.ModuleId, out var definition) && definition is StorageModuleDefinition storageDefinition
|
||||
? (Module: module, Definition: storageDefinition)
|
||||
: ((StorageStationModuleRuntime Module, StorageModuleDefinition Definition)?)null)
|
||||
.Where(entry => entry is not null && entry.Value.Definition.StorageKind == storageKind)
|
||||
.Select(entry => entry!.Value);
|
||||
|
||||
private static void SyncStorageModuleLevels(SimulationWorld world, StationRuntime station, StorageKind storageKind)
|
||||
{
|
||||
var storageModules = GetStorageModules(world, station, storageKind)
|
||||
.OrderBy(entry => entry.Module.Id, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
if (storageModules.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var remaining = station.Inventory
|
||||
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoStorageKind == storageKind)
|
||||
.Sum(entry => entry.Value);
|
||||
|
||||
foreach (var (module, definition) in storageModules)
|
||||
{
|
||||
module.CurrentLevel = MathF.Min(remaining, definition.StorageCapacity);
|
||||
remaining = MathF.Max(0f, remaining - definition.StorageCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AddStationModule(SimulationWorld world, StationRuntime station, string moduleId)
|
||||
{
|
||||
@@ -21,13 +63,7 @@ internal static class SimulationRuntimeSupport
|
||||
return;
|
||||
}
|
||||
|
||||
station.Modules.Add(new StationModuleRuntime
|
||||
{
|
||||
Id = $"{station.Id}-module-{station.Modules.Count + 1}",
|
||||
ModuleId = moduleId,
|
||||
Health = definition.Hull,
|
||||
MaxHealth = definition.Hull,
|
||||
});
|
||||
station.Modules.Add(StationModuleRuntime.Create($"{station.Id}-module-{station.Modules.Count + 1}", definition));
|
||||
station.Radius = GetStationRadius(world, station);
|
||||
}
|
||||
|
||||
@@ -39,41 +75,9 @@ internal static class SimulationRuntimeSupport
|
||||
return MathF.Max(24f, MathF.Sqrt(MathF.Max(totalArea, 1f)));
|
||||
}
|
||||
|
||||
internal static float GetStationStorageCapacity(StationRuntime station, string storageClass)
|
||||
{
|
||||
var baseCapacity = storageClass switch
|
||||
{
|
||||
"manufactured" => 400f,
|
||||
_ => 0f,
|
||||
};
|
||||
|
||||
var bulkBays = CountStationModules(station, "module_arg_stor_solid_m_01");
|
||||
var liquidTanks = CountStationModules(station, "module_arg_stor_liquid_m_01");
|
||||
var containerBays = CountStationModules(station, "module_arg_stor_container_m_01");
|
||||
|
||||
var moduleCapacity = storageClass switch
|
||||
{
|
||||
"solid" => bulkBays * 1000f,
|
||||
"liquid" => liquidTanks * 500f,
|
||||
"container" => containerBays * 800f,
|
||||
"manufactured" => containerBays * 200f,
|
||||
_ => 0f,
|
||||
};
|
||||
|
||||
return baseCapacity + moduleCapacity;
|
||||
}
|
||||
|
||||
internal static int CountModules(IEnumerable<string> modules, string moduleId) =>
|
||||
modules.Count(candidate => string.Equals(candidate, moduleId, StringComparison.Ordinal));
|
||||
|
||||
internal static int CountModules(
|
||||
IEnumerable<string> modules,
|
||||
IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions,
|
||||
ModuleType moduleType) =>
|
||||
modules.Count(moduleId =>
|
||||
moduleDefinitions.TryGetValue(moduleId, out var definition)
|
||||
&& definition.ModuleType == moduleType);
|
||||
|
||||
internal static float GetInventoryAmount(IReadOnlyDictionary<string, float> inventory, string itemId) =>
|
||||
inventory.TryGetValue(itemId, out var amount) ? amount : 0f;
|
||||
|
||||
@@ -110,7 +114,8 @@ internal static class SimulationRuntimeSupport
|
||||
internal 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);
|
||||
&& item.CargoStorageKind is not null
|
||||
&& item.CargoStorageKind == ship.Definition.CargoStorageKind;
|
||||
|
||||
internal static bool CanBuildClaimBeacon(ShipRuntime ship) =>
|
||||
string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal);
|
||||
@@ -126,13 +131,43 @@ internal static class SimulationRuntimeSupport
|
||||
return 0.1f + (0.9f * staffedRatio);
|
||||
}
|
||||
|
||||
internal static string? GetStorageRequirement(string storageClass) =>
|
||||
storageClass switch
|
||||
internal static string? GetStorageRequirement(
|
||||
IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions,
|
||||
StorageKind? storageKind)
|
||||
{
|
||||
if (storageKind is not { } requiredStorageKind)
|
||||
{
|
||||
"solid" => "module_arg_stor_solid_m_01",
|
||||
"liquid" => "module_arg_stor_liquid_m_01",
|
||||
_ => null,
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
return moduleDefinitions.Values
|
||||
.OfType<StorageModuleDefinition>()
|
||||
.Where(definition => definition.StorageKind == requiredStorageKind)
|
||||
.OrderBy(definition => GetPreferredStorageModuleRank(definition.Id))
|
||||
.ThenBy(definition => definition.Id, StringComparer.Ordinal)
|
||||
.Select(definition => definition.Id)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static int GetPreferredStorageModuleRank(string moduleId)
|
||||
{
|
||||
if (moduleId.Contains("_m_", StringComparison.Ordinal))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (moduleId.Contains("_s_", StringComparison.Ordinal))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (moduleId.Contains("_l_", StringComparison.Ordinal))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
internal static float TryAddStationInventory(SimulationWorld world, StationRuntime station, string itemId, float amount)
|
||||
{
|
||||
@@ -141,21 +176,25 @@ internal static class SimulationRuntimeSupport
|
||||
return 0f;
|
||||
}
|
||||
|
||||
var storageClass = itemDefinition.CargoKind;
|
||||
var requiredModule = GetStorageRequirement(storageClass);
|
||||
if (requiredModule is not null && !station.InstalledModules.Contains(requiredModule, StringComparer.Ordinal))
|
||||
var storageKind = itemDefinition.CargoStorageKind;
|
||||
if (storageKind is null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
var capacity = GetStationStorageCapacity(station, storageClass);
|
||||
if (!HasStorageCapacity(world, station, storageKind.Value))
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
var capacity = GetStationStorageCapacity(world, station, storageKind.Value);
|
||||
if (capacity <= 0.01f)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
var used = station.Inventory
|
||||
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoKind == storageClass)
|
||||
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoStorageKind == storageKind)
|
||||
.Sum(entry => entry.Value);
|
||||
var accepted = MathF.Min(amount, MathF.Max(0f, capacity - used));
|
||||
if (accepted <= 0.01f)
|
||||
|
||||
Reference in New Issue
Block a user