Files
space-game/apps/backend/Universe/Scenario/StarterStationLayoutResolver.cs

151 lines
5.4 KiB
C#

using SpaceGame.Api.Shared.Runtime;
namespace SpaceGame.Api.Universe.Scenario;
internal static class StarterStationLayoutResolver
{
internal static string ResolveDockModuleId(
string? factionId,
IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions) =>
SelectPreferredModule(
moduleDefinitions.Values.Where(definition => definition.ModuleType == ModuleType.DockArea),
factionId,
"starter dock module").Id;
internal static string ResolvePowerModuleId(
string? factionId,
IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions) =>
ResolveProducerModuleId("energycells", factionId, moduleDefinitions);
internal static string? ResolveObjectiveModuleId(
string? objective,
string? factionId,
IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions)
{
var targetWareId = ResolveObjectiveWareId(objective);
return targetWareId is null ? null : ResolveProducerModuleId(targetWareId, factionId, moduleDefinitions);
}
internal static IEnumerable<string> ResolveRequiredStorageModuleIds(
string moduleId,
string? factionId,
IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions,
IReadOnlyDictionary<string, ItemDefinition> itemDefinitions,
IReadOnlyDictionary<string, RecipeDefinition> recipes)
{
if (!moduleDefinitions.TryGetValue(moduleId, out var moduleDefinition))
{
throw new InvalidOperationException($"Module '{moduleId}' is not defined in static data.");
}
foreach (var wareId in moduleDefinition.BuildRecipes
.SelectMany(production => production.Wares.Select(ware => ware.ItemId))
.Concat(moduleDefinition.ProductItemIds)
.Concat(recipes.Values
.Where(recipe => recipe.RequiredModules.Contains(moduleId, StringComparer.Ordinal))
.SelectMany(recipe => recipe.Inputs.Select(input => input.ItemId)
.Concat(recipe.Outputs.Select(output => output.ItemId))))
.Distinct(StringComparer.Ordinal))
{
if (!itemDefinitions.TryGetValue(wareId, out var itemDefinition))
{
throw new InvalidOperationException($"Module '{moduleId}' references unknown ware '{wareId}'.");
}
if (itemDefinition.CargoKind is not { } storageKind)
{
continue;
}
yield return ResolveStorageModuleId(storageKind, factionId, moduleDefinitions);
}
}
private static string? ResolveObjectiveWareId(string? objective) =>
StationSimulationService.NormalizeStationObjective(objective) switch
{
"power" => "energycells",
"refinery" => "refinedmetals",
"graphene" => "graphene",
"siliconwafers" => "siliconwafers",
"hullparts" => "hullparts",
"claytronics" => "claytronics",
"quantumtubes" => "quantumtubes",
"antimattercells" => "antimattercells",
"superfluidcoolant" => "superfluidcoolant",
"water" => "water",
_ => null,
};
private static string ResolveProducerModuleId(
string wareId,
string? factionId,
IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions) =>
SelectPreferredModule(
moduleDefinitions.Values
.OfType<ProductionModuleDefinition>()
.Where(definition => definition.ProductItemIds.Contains(wareId, StringComparer.Ordinal)),
factionId,
$"producer module for ware '{wareId}'").Id;
private static string ResolveStorageModuleId(
StorageKind storageKind,
string? factionId,
IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions) =>
SelectPreferredModule(
moduleDefinitions.Values
.OfType<StorageModuleDefinition>()
.Where(definition => definition.StorageKind == storageKind),
factionId,
$"storage module for cargo kind '{storageKind.ToDataValue()}'").Id;
private static T SelectPreferredModule<T>(
IEnumerable<T> candidates,
string? factionId,
string context)
where T : ModuleDefinition
{
var ordered = candidates
.OrderBy(definition => ComputeOwnerRank(definition, factionId))
.ThenBy(definition => ComputeModuleRank(definition))
.ThenBy(definition => definition.Id, StringComparer.Ordinal)
.ToList();
return ordered.FirstOrDefault()
?? throw new InvalidOperationException($"Unable to resolve {context}.");
}
private static int ComputeOwnerRank(ModuleDefinition definition, string? factionId)
{
if (string.IsNullOrWhiteSpace(factionId))
{
return 1;
}
return definition.Owners.Contains(factionId, StringComparer.Ordinal) ? 0 : 1;
}
private static int ComputeModuleRank(ModuleDefinition definition)
{
if (definition.ModuleType is ModuleType.DockArea or ModuleType.Storage)
{
if (definition.Id.Contains("_m_", StringComparison.Ordinal))
{
return 0;
}
if (definition.Id.Contains("_s_", StringComparison.Ordinal))
{
return 1;
}
if (definition.Id.Contains("_l_", StringComparison.Ordinal))
{
return 2;
}
}
return 3;
}
}