feat: improved visualisation and x4 data import
This commit is contained in:
@@ -12,7 +12,6 @@ public sealed partial class ScenarioLoader
|
||||
private const float MinimumRefineryStock = 0f;
|
||||
private const float MinimumShipyardStock = 0f;
|
||||
private const float MinimumSystemSeparation = 3.2f;
|
||||
private const float StarBubbleRadiusPadding = 40f;
|
||||
private const float LocalSpaceRadius = 10_000f;
|
||||
private static readonly string[] GeneratedSystemNames =
|
||||
[
|
||||
@@ -51,13 +50,13 @@ public sealed partial class ScenarioLoader
|
||||
];
|
||||
private static readonly StarProfile[] StarProfiles =
|
||||
[
|
||||
new("main-sequence", "#ffd27a", "#ffb14a", 696340f, 1),
|
||||
new("blue-white", "#9dc6ff", "#66a0ff", 930000f, 1),
|
||||
new("white-dwarf", "#f1f5ff", "#b8caff", 12000f, 1),
|
||||
new("brown-dwarf", "#b97d56", "#8a5438", 70000f, 1),
|
||||
new("neutron-star", "#d9ebff", "#7ab4ff", 18f, 1),
|
||||
new("binary-main-sequence", "#ffe09f", "#ffbe6b", 780000f, 2),
|
||||
new("binary-white-dwarf", "#edf3ff", "#c8d6ff", 14000f, 2),
|
||||
new("main-sequence", "#ffd27a", "#ffb14a", 696340f),
|
||||
new("blue-white", "#9dc6ff", "#66a0ff", 930000f),
|
||||
new("white-dwarf", "#f1f5ff", "#b8caff", 12000f),
|
||||
new("brown-dwarf", "#b97d56", "#8a5438", 70000f),
|
||||
new("neutron-star", "#d9ebff", "#7ab4ff", 18f),
|
||||
new("binary-main-sequence", "#ffe09f", "#ffbe6b", 780000f),
|
||||
new("binary-white-dwarf", "#edf3ff", "#c8d6ff", 14000f),
|
||||
];
|
||||
private static readonly PlanetProfile[] PlanetProfiles =
|
||||
[
|
||||
@@ -88,16 +87,16 @@ public sealed partial class ScenarioLoader
|
||||
{
|
||||
var authoredSystems = Read<List<SolarSystemDefinition>>("systems.json");
|
||||
var systems = ExpandSystems(
|
||||
InjectSpecialSystems(authoredSystems, _worldGeneration.IncludeSolSystem),
|
||||
InjectSpecialSystems(authoredSystems),
|
||||
_worldGeneration.TargetSystemCount);
|
||||
var scenario = NormalizeScenarioToAvailableSystems(
|
||||
Read<ScenarioDefinition>("scenario.json"),
|
||||
systems.Select((system) => system.Id).ToList());
|
||||
var modules = Read<List<ModuleDefinition>>("modules.json");
|
||||
var modules = NormalizeModules(Read<List<ModuleDefinition>>("modules.json"));
|
||||
var ships = Read<List<ShipDefinition>>("ships.json");
|
||||
var items = Read<List<ItemDefinition>>("items.json");
|
||||
var items = NormalizeItems(Read<List<ItemDefinition>>("items.json"));
|
||||
var balance = Read<BalanceDefinition>("balance.json");
|
||||
var recipes = BuildRecipes(items, ships);
|
||||
var recipes = BuildRecipes(items, ships, modules);
|
||||
var moduleRecipes = BuildModuleRecipes(modules);
|
||||
|
||||
var moduleDefinitions = modules.ToDictionary((definition) => definition.Id, StringComparer.Ordinal);
|
||||
@@ -177,7 +176,7 @@ public sealed partial class ScenarioLoader
|
||||
|
||||
var startingModules = plan.StartingModules.Count > 0
|
||||
? plan.StartingModules
|
||||
: ["dock-bay-small", "power-core", "bulk-bay", "liquid-tank"];
|
||||
: ["module_arg_dock_m_01_lowtech", "module_gen_prod_energycells_01", "module_arg_stor_solid_m_01", "module_arg_stor_liquid_m_01"];
|
||||
foreach (var moduleId in startingModules)
|
||||
{
|
||||
AddStationModule(stations[^1], moduleDefinitions, moduleId);
|
||||
@@ -187,7 +186,7 @@ public sealed partial class ScenarioLoader
|
||||
foreach (var station in stations)
|
||||
{
|
||||
InitializeStationPopulation(station);
|
||||
station.Inventory["refined-metals"] = 120f;
|
||||
station.Inventory["refinedmetals"] = 120f;
|
||||
if (station.Population > 0f)
|
||||
{
|
||||
station.Inventory["water"] = MathF.Max(80f, station.Population * 1.5f);
|
||||
@@ -195,9 +194,9 @@ public sealed partial class ScenarioLoader
|
||||
}
|
||||
|
||||
var refinery = stations.FirstOrDefault((station) =>
|
||||
HasInstalledModules(station, "power-core", "liquid-tank") &&
|
||||
HasInstalledModules(station, "module_gen_prod_energycells_01", "module_arg_stor_liquid_m_01") &&
|
||||
station.SystemId == scenario.MiningDefaults.RefinerySystemId)
|
||||
?? stations.FirstOrDefault((station) => HasInstalledModules(station, "power-core", "liquid-tank"));
|
||||
?? stations.FirstOrDefault((station) => HasInstalledModules(station, "module_gen_prod_energycells_01", "module_arg_stor_liquid_m_01"));
|
||||
|
||||
var patrolRoutes = scenario.PatrolRoutes
|
||||
.GroupBy((route) => route.SystemId, StringComparer.Ordinal)
|
||||
@@ -400,12 +399,12 @@ public sealed partial class ScenarioLoader
|
||||
|
||||
private static List<ModuleRecipeDefinition> BuildModuleRecipes(IEnumerable<ModuleDefinition> modules) =>
|
||||
modules
|
||||
.Where((module) => module.Construction is not null)
|
||||
.Where((module) => module.Construction is not null || module.Production.Count > 0)
|
||||
.Select((module) => new ModuleRecipeDefinition
|
||||
{
|
||||
ModuleId = module.Id,
|
||||
Duration = module.Construction!.ProductionTime,
|
||||
Inputs = module.Construction.Requirements
|
||||
Duration = module.Construction?.ProductionTime ?? module.Production[0].Time,
|
||||
Inputs = (module.Construction?.Requirements ?? module.Production[0].Wares)
|
||||
.Select((input) => new RecipeInputDefinition
|
||||
{
|
||||
ItemId = input.ItemId,
|
||||
@@ -415,12 +414,54 @@ public sealed partial class ScenarioLoader
|
||||
})
|
||||
.ToList();
|
||||
|
||||
private static List<RecipeDefinition> BuildRecipes(IEnumerable<ItemDefinition> items, IEnumerable<ShipDefinition> ships)
|
||||
private static List<RecipeDefinition> BuildRecipes(IEnumerable<ItemDefinition> items, IEnumerable<ShipDefinition> ships, IReadOnlyCollection<ModuleDefinition> modules)
|
||||
{
|
||||
var recipes = new List<RecipeDefinition>();
|
||||
var preferredProducerByItemId = modules
|
||||
.Where((module) => module.Products.Count > 0)
|
||||
.GroupBy((module) => module.Products[0], StringComparer.Ordinal)
|
||||
.ToDictionary(
|
||||
(group) => group.Key,
|
||||
(group) => group.OrderBy((module) => module.Id, StringComparer.Ordinal).First().Id,
|
||||
StringComparer.Ordinal);
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Production.Count > 0)
|
||||
{
|
||||
foreach (var production in item.Production)
|
||||
{
|
||||
recipes.Add(new RecipeDefinition
|
||||
{
|
||||
Id = $"{item.Id}-{production.Method}-production",
|
||||
Label = production.Name == "Universal"
|
||||
? item.Name
|
||||
: $"{item.Name} ({production.Name})",
|
||||
FacilityCategory = InferFacilityCategory(item),
|
||||
Duration = production.Time,
|
||||
Priority = InferRecipePriority(item),
|
||||
RequiredModules = InferRequiredModules(item, preferredProducerByItemId),
|
||||
Inputs = production.Wares
|
||||
.Select((input) => new RecipeInputDefinition
|
||||
{
|
||||
ItemId = input.ItemId,
|
||||
Amount = input.Amount,
|
||||
})
|
||||
.ToList(),
|
||||
Outputs =
|
||||
[
|
||||
new RecipeOutputDefinition
|
||||
{
|
||||
ItemId = item.Id,
|
||||
Amount = production.Amount,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.Construction is null)
|
||||
{
|
||||
continue;
|
||||
@@ -481,6 +522,74 @@ public sealed partial class ScenarioLoader
|
||||
return recipes;
|
||||
}
|
||||
|
||||
private static string InferFacilityCategory(ItemDefinition item) =>
|
||||
item.Group switch
|
||||
{
|
||||
"agricultural" or "food" or "pharmaceutical" or "water" => "farm",
|
||||
_ => "station",
|
||||
};
|
||||
|
||||
private static List<string> InferRequiredModules(ItemDefinition item, IReadOnlyDictionary<string, string> preferredProducerByItemId)
|
||||
{
|
||||
if (preferredProducerByItemId.TryGetValue(item.Id, out var moduleId))
|
||||
{
|
||||
return [moduleId];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private static int InferRecipePriority(ItemDefinition item) =>
|
||||
item.Group switch
|
||||
{
|
||||
"energy" => 140,
|
||||
"water" => 130,
|
||||
"food" => 120,
|
||||
"agricultural" => 110,
|
||||
"refined" => 100,
|
||||
"hightech" => 90,
|
||||
"shiptech" => 80,
|
||||
"pharmaceutical" => 70,
|
||||
_ => 60,
|
||||
};
|
||||
|
||||
private static List<ItemDefinition> NormalizeItems(List<ItemDefinition> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(item.Type))
|
||||
{
|
||||
item.Type = string.IsNullOrWhiteSpace(item.Group) ? "material" : item.Group;
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private static List<ModuleDefinition> NormalizeModules(List<ModuleDefinition> modules)
|
||||
{
|
||||
foreach (var module in modules)
|
||||
{
|
||||
if (module.Products.Count == 0 && !string.IsNullOrWhiteSpace(module.Product))
|
||||
{
|
||||
module.Products = [module.Product];
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(module.ProductionMode))
|
||||
{
|
||||
module.ProductionMode = string.Equals(module.Type, "buildmodule", StringComparison.Ordinal)
|
||||
? "commanded"
|
||||
: "passive";
|
||||
}
|
||||
|
||||
if (module.WorkforceNeeded <= 0f)
|
||||
{
|
||||
module.WorkforceNeeded = module.WorkForce?.Max ?? 0f;
|
||||
}
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
private static Vector3 Scale(Vector3 vector, float scale) => new(vector.X * scale, vector.Y * scale, vector.Z * scale);
|
||||
|
||||
private static float DegreesToRadians(float degrees) => degrees * (MathF.PI / 180f);
|
||||
|
||||
Reference in New Issue
Block a user