234 lines
9.1 KiB
C#
234 lines
9.1 KiB
C#
|
|
namespace SpaceGame.Api.Shared.Runtime;
|
|
|
|
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, ModuleType moduleType) =>
|
|
station.Modules.Count(module => module.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.CargoKind == 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)
|
|
{
|
|
if (!world.ModuleDefinitions.TryGetValue(moduleId, out var definition))
|
|
{
|
|
return;
|
|
}
|
|
|
|
station.Modules.Add(StationModuleRuntime.Create($"{station.Id}-module-{station.Modules.Count + 1}", definition));
|
|
station.Radius = GetStationRadius(world, station);
|
|
}
|
|
|
|
internal static float GetStationRadius(SimulationWorld world, StationRuntime station)
|
|
{
|
|
var totalArea = station.Modules
|
|
.Select(module => world.ModuleDefinitions.TryGetValue(module.ModuleId, out var definition) ? definition.Radius * definition.Radius : 0f)
|
|
.Sum();
|
|
return MathF.Max(24f, MathF.Sqrt(MathF.Max(totalArea, 1f)));
|
|
}
|
|
|
|
internal static int CountModules(IEnumerable<string> modules, string moduleId) =>
|
|
modules.Count(candidate => string.Equals(candidate, moduleId, StringComparison.Ordinal));
|
|
|
|
internal static float GetInventoryAmount(IReadOnlyDictionary<string, float> inventory, string itemId) =>
|
|
inventory.TryGetValue(itemId, out var amount) ? amount : 0f;
|
|
|
|
internal static void AddInventory(IDictionary<string, float> inventory, string itemId, float amount)
|
|
{
|
|
if (amount <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
inventory[itemId] = GetInventoryAmount((IReadOnlyDictionary<string, float>)inventory, itemId) + amount;
|
|
}
|
|
|
|
internal static float RemoveInventory(IDictionary<string, float> inventory, string itemId, float amount)
|
|
{
|
|
var current = GetInventoryAmount((IReadOnlyDictionary<string, float>)inventory, itemId);
|
|
var removed = MathF.Min(current, amount);
|
|
var remaining = current - removed;
|
|
if (remaining <= 0.001f)
|
|
{
|
|
inventory.Remove(itemId);
|
|
}
|
|
else
|
|
{
|
|
inventory[itemId] = remaining;
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
internal static bool HasStationModules(StationRuntime station, params string[] modules) =>
|
|
modules.All(moduleId => station.Modules.Any(candidate => string.Equals(candidate.ModuleId, moduleId, StringComparison.Ordinal)));
|
|
|
|
internal static bool CanExtractNode(ShipRuntime ship, ResourceNodeRuntime node, SimulationWorld world) =>
|
|
HasShipCapabilities(ship.Definition, "mining")
|
|
&& world.ItemDefinitions.TryGetValue(node.ItemId, out var item)
|
|
&& item.CargoKind is not null
|
|
&& item.CargoKind == ship.Definition.CargoKind;
|
|
|
|
internal static bool CanBuildClaimBeacon(ShipRuntime ship) =>
|
|
string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal);
|
|
|
|
internal static float ComputeWorkforceRatio(float population, float workforceRequired)
|
|
{
|
|
if (workforceRequired <= 0.01f)
|
|
{
|
|
return 1f;
|
|
}
|
|
|
|
var staffedRatio = MathF.Min(1f, population / workforceRequired);
|
|
return 0.1f + (0.9f * staffedRatio);
|
|
}
|
|
|
|
internal static string? GetStorageRequirement(
|
|
IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions,
|
|
StorageKind? storageKind)
|
|
{
|
|
if (storageKind is not { } requiredStorageKind)
|
|
{
|
|
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)
|
|
{
|
|
if (amount <= 0f || !world.ItemDefinitions.TryGetValue(itemId, out var itemDefinition))
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
var storageKind = itemDefinition.CargoKind;
|
|
if (storageKind is null)
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
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 == storageKind)
|
|
.Sum(entry => entry.Value);
|
|
var accepted = MathF.Min(amount, MathF.Max(0f, capacity - used));
|
|
if (accepted <= 0.01f)
|
|
{
|
|
return 0f;
|
|
}
|
|
|
|
AddInventory(station.Inventory, itemId, accepted);
|
|
return accepted;
|
|
}
|
|
|
|
internal static bool CanStartModuleConstruction(StationRuntime station, ModuleRecipeDefinition recipe) =>
|
|
recipe.Inputs.All(input => GetInventoryAmount(station.Inventory, input.ItemId) + 0.001f >= input.Amount);
|
|
|
|
internal static ConstructionSiteRuntime? GetConstructionSiteForStation(SimulationWorld world, string stationId) =>
|
|
world.ConstructionSites.FirstOrDefault(site =>
|
|
string.Equals(site.StationId, stationId, StringComparison.Ordinal)
|
|
&& site.State is not ConstructionSiteStateKinds.Completed and not ConstructionSiteStateKinds.Destroyed);
|
|
|
|
internal static float GetConstructionDeliveredAmount(SimulationWorld world, ConstructionSiteRuntime site, string itemId)
|
|
{
|
|
if (site.StationId is not null
|
|
&& world.Stations.FirstOrDefault(candidate => candidate.Id == site.StationId) is { } station)
|
|
{
|
|
return GetInventoryAmount(station.Inventory, itemId);
|
|
}
|
|
|
|
return GetInventoryAmount(site.DeliveredItems, itemId);
|
|
}
|
|
|
|
internal static bool IsConstructionSiteReady(SimulationWorld world, ConstructionSiteRuntime site) =>
|
|
site.RequiredItems.All(entry => GetConstructionDeliveredAmount(world, site, entry.Key) + 0.001f >= entry.Value);
|
|
|
|
internal static float GetShipCargoAmount(ShipRuntime ship) =>
|
|
ship.Inventory.Values.Sum();
|
|
}
|