using SpaceGame.Api.Shared.Runtime; namespace SpaceGame.Api.Universe.Scenario; internal static class StarterStationLayoutResolver { internal static string ResolveDockModuleId( string? factionId, IReadOnlyDictionary moduleDefinitions) => SelectPreferredModule( moduleDefinitions.Values.Where(definition => definition.ModuleType == ModuleType.DockArea), factionId, "starter dock module").Id; internal static string ResolvePowerModuleId( string? factionId, IReadOnlyDictionary moduleDefinitions) => ResolveProducerModuleId("energycells", factionId, moduleDefinitions); internal static string? ResolveObjectiveModuleId( string? objective, string? factionId, IReadOnlyDictionary moduleDefinitions) { var targetWareId = ResolveObjectiveWareId(objective); return targetWareId is null ? null : ResolveProducerModuleId(targetWareId, factionId, moduleDefinitions); } internal static IEnumerable ResolveRequiredStorageModuleIds( string moduleId, string? factionId, IReadOnlyDictionary moduleDefinitions, IReadOnlyDictionary itemDefinitions) { 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) .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 moduleDefinitions) => SelectPreferredModule( moduleDefinitions.Values .OfType() .Where(definition => definition.ProductItemIds.Contains(wareId, StringComparer.Ordinal)), factionId, $"producer module for ware '{wareId}'").Id; private static string ResolveStorageModuleId( StorageKind storageKind, string? factionId, IReadOnlyDictionary moduleDefinitions) => SelectPreferredModule( moduleDefinitions.Values .OfType() .Where(definition => definition.StorageKind == storageKind), factionId, $"storage module for cargo kind '{storageKind.ToDataValue()}'").Id; private static T SelectPreferredModule( IEnumerable 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; } }