feat: improved visualisation and x4 data import
This commit is contained in:
@@ -1,20 +1,36 @@
|
||||
namespace SpaceGame.Simulation.Api.Contracts;
|
||||
|
||||
public sealed record StarSnapshot(
|
||||
string Kind,
|
||||
string Color,
|
||||
string Glow,
|
||||
float Size,
|
||||
float OrbitRadius,
|
||||
float OrbitSpeed,
|
||||
float OrbitPhaseAtEpoch);
|
||||
|
||||
public sealed record MoonSnapshot(
|
||||
string Label,
|
||||
float Size,
|
||||
string Color,
|
||||
float OrbitRadius,
|
||||
float OrbitSpeed,
|
||||
float OrbitPhaseAtEpoch,
|
||||
float OrbitInclination,
|
||||
float OrbitLongitudeOfAscendingNode);
|
||||
|
||||
public sealed record SystemSnapshot(
|
||||
string Id,
|
||||
string Label,
|
||||
Vector3Dto GalaxyPosition,
|
||||
string StarKind,
|
||||
int StarCount,
|
||||
string StarColor,
|
||||
float StarSize,
|
||||
IReadOnlyList<StarSnapshot> Stars,
|
||||
IReadOnlyList<PlanetSnapshot> Planets);
|
||||
|
||||
public sealed record PlanetSnapshot(
|
||||
string Label,
|
||||
string PlanetType,
|
||||
string Shape,
|
||||
int MoonCount,
|
||||
IReadOnlyList<MoonSnapshot> Moons,
|
||||
float OrbitRadius,
|
||||
float OrbitSpeed,
|
||||
float OrbitEccentricity,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SpaceGame.Simulation.Api.Data;
|
||||
|
||||
public sealed class ConstructionDefinition
|
||||
@@ -13,6 +15,29 @@ public sealed class ConstructionDefinition
|
||||
public int Priority { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ItemPriceDefinition
|
||||
{
|
||||
public float Min { get; set; }
|
||||
public float Max { get; set; }
|
||||
public float Avg { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ItemEffectDefinition
|
||||
{
|
||||
public required string Type { get; set; }
|
||||
public float Product { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ItemProductionDefinition
|
||||
{
|
||||
public float Time { get; set; }
|
||||
public float Amount { get; set; }
|
||||
public string Method { get; set; } = "default";
|
||||
public string Name { get; set; } = "Universal";
|
||||
public List<RecipeInputDefinition> Wares { get; set; } = [];
|
||||
public List<ItemEffectDefinition> Effects { get; set; } = [];
|
||||
}
|
||||
|
||||
public sealed class BalanceDefinition
|
||||
{
|
||||
public float YPlane { get; set; }
|
||||
@@ -25,17 +50,35 @@ public sealed class BalanceDefinition
|
||||
public float UndockDistance { get; set; }
|
||||
}
|
||||
|
||||
public sealed class StarDefinition
|
||||
{
|
||||
public string Kind { get; set; } = "main-sequence";
|
||||
public required string Color { get; set; }
|
||||
public required string Glow { get; set; }
|
||||
public float Size { get; set; }
|
||||
public float OrbitRadius { get; set; }
|
||||
public float OrbitSpeed { get; set; }
|
||||
public float OrbitPhaseAtEpoch { get; set; }
|
||||
}
|
||||
|
||||
public sealed class MoonDefinition
|
||||
{
|
||||
public required string Label { get; set; }
|
||||
public float Size { get; set; }
|
||||
public required string Color { get; set; }
|
||||
public float OrbitRadius { get; set; }
|
||||
public float OrbitSpeed { get; set; }
|
||||
public float OrbitPhaseAtEpoch { get; set; }
|
||||
public float OrbitInclination { get; set; }
|
||||
public float OrbitLongitudeOfAscendingNode { get; set; }
|
||||
}
|
||||
|
||||
public sealed class SolarSystemDefinition
|
||||
{
|
||||
public required string Id { get; set; }
|
||||
public required string Label { get; set; }
|
||||
public required float[] Position { get; set; }
|
||||
public string StarKind { get; set; } = "main-sequence";
|
||||
public int StarCount { get; set; } = 1;
|
||||
public required string StarColor { get; set; }
|
||||
public required string StarGlow { get; set; }
|
||||
public float StarSize { get; set; }
|
||||
public float GravityWellRadius { get; set; }
|
||||
public required List<StarDefinition> Stars { get; set; }
|
||||
public required AsteroidFieldDefinition AsteroidField { get; set; }
|
||||
public required List<ResourceNodeDefinition> ResourceNodes { get; set; }
|
||||
public required List<PlanetDefinition> Planets { get; set; }
|
||||
@@ -68,9 +111,21 @@ public sealed class ItemDefinition
|
||||
public required string Name { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public string Type { get; set; } = "material";
|
||||
public required string CargoKind { get; set; }
|
||||
public string CargoKind { get; set; } = string.Empty;
|
||||
public float Volume { get; set; } = 1f;
|
||||
public int Version { get; set; }
|
||||
public string FactoryName { get; set; } = string.Empty;
|
||||
public string Icon { get; set; } = string.Empty;
|
||||
public string Group { get; set; } = string.Empty;
|
||||
public ItemPriceDefinition? Price { get; set; }
|
||||
public List<string> Illegal { get; set; } = [];
|
||||
public List<ItemProductionDefinition> Production { get; set; } = [];
|
||||
public ConstructionDefinition? Construction { get; set; }
|
||||
[JsonPropertyName("transport")]
|
||||
public string Transport
|
||||
{
|
||||
set => CargoKind = value;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RecipeOutputDefinition
|
||||
@@ -81,8 +136,13 @@ public sealed class RecipeOutputDefinition
|
||||
|
||||
public sealed class RecipeInputDefinition
|
||||
{
|
||||
public required string ItemId { get; set; }
|
||||
public string ItemId { get; set; } = string.Empty;
|
||||
public float Amount { get; set; }
|
||||
[JsonPropertyName("ware")]
|
||||
public string Ware
|
||||
{
|
||||
set => ItemId = value;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ModuleConstructionDefinition
|
||||
@@ -91,18 +151,73 @@ public sealed class ModuleConstructionDefinition
|
||||
public float ProductionTime { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ModuleDockDefinition
|
||||
{
|
||||
public int Capacity { get; set; }
|
||||
public required string Size { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ModuleCargoDefinition
|
||||
{
|
||||
public float Max { get; set; }
|
||||
public required string Type { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ModuleWorkForceDefinition
|
||||
{
|
||||
public float Capacity { get; set; }
|
||||
public float Max { get; set; }
|
||||
public string Race { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class ModuleMountDefinition
|
||||
{
|
||||
public required string Group { get; set; }
|
||||
public required string Size { get; set; }
|
||||
public bool Hittable { get; set; }
|
||||
public List<string> Types { get; set; } = [];
|
||||
}
|
||||
|
||||
public sealed class ModuleProductionDefinition
|
||||
{
|
||||
public float Time { get; set; }
|
||||
public float Amount { get; set; }
|
||||
public string Method { get; set; } = "default";
|
||||
public string Name { get; set; } = "Universal";
|
||||
public List<RecipeInputDefinition> Wares { get; set; } = [];
|
||||
}
|
||||
|
||||
public sealed class ModuleDefinition
|
||||
{
|
||||
public required string Id { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public required string Type { get; set; }
|
||||
[JsonIgnore]
|
||||
public string? Product { get; set; }
|
||||
public List<string> Products { get; set; } = [];
|
||||
public string ProductionMode { get; set; } = "passive";
|
||||
public float Radius { get; set; } = 12f;
|
||||
public float Hull { get; set; } = 100f;
|
||||
public float WorkforceNeeded { get; set; }
|
||||
public int Version { get; set; }
|
||||
public string Macro { get; set; } = string.Empty;
|
||||
public string MakerRace { get; set; } = string.Empty;
|
||||
public int ExplosionDamage { get; set; }
|
||||
public ItemPriceDefinition? Price { get; set; }
|
||||
public List<string> Owners { get; set; } = [];
|
||||
public ModuleCargoDefinition? Cargo { get; set; }
|
||||
public ModuleWorkForceDefinition? WorkForce { get; set; }
|
||||
public List<ModuleDockDefinition> Docks { get; set; } = [];
|
||||
public List<ModuleMountDefinition> Shields { get; set; } = [];
|
||||
public List<ModuleMountDefinition> Turrets { get; set; } = [];
|
||||
public List<ModuleProductionDefinition> Production { get; set; } = [];
|
||||
public ModuleConstructionDefinition? Construction { get; set; }
|
||||
[JsonPropertyName("product")]
|
||||
public List<string> ProductIds
|
||||
{
|
||||
set => Products = value ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ModuleRecipeDefinition
|
||||
@@ -130,7 +245,7 @@ public sealed class PlanetDefinition
|
||||
public required string Label { get; set; }
|
||||
public string PlanetType { get; set; } = "terrestrial";
|
||||
public string Shape { get; set; } = "sphere";
|
||||
public int MoonCount { get; set; }
|
||||
public List<MoonDefinition> Moons { get; set; } = [];
|
||||
public float OrbitRadius { get; set; }
|
||||
public float OrbitSpeed { get; set; }
|
||||
public float OrbitEccentricity { get; set; }
|
||||
|
||||
@@ -8,19 +8,11 @@ public sealed partial class ScenarioLoader
|
||||
private const string DevelopmentCompanionSystemId = "helios";
|
||||
|
||||
private static List<SolarSystemDefinition> InjectSpecialSystems(
|
||||
IReadOnlyList<SolarSystemDefinition> authoredSystems,
|
||||
bool includeSolSystem)
|
||||
IReadOnlyList<SolarSystemDefinition> authoredSystems)
|
||||
{
|
||||
var systems = authoredSystems
|
||||
return authoredSystems
|
||||
.Select(CloneSystemDefinition)
|
||||
.ToList();
|
||||
|
||||
if (includeSolSystem && systems.All((system) => system.Id != "sol"))
|
||||
{
|
||||
systems.Add(CreateSolSystem());
|
||||
}
|
||||
|
||||
return systems;
|
||||
}
|
||||
|
||||
private static List<SolarSystemDefinition> ExpandSystems(
|
||||
@@ -156,12 +148,16 @@ public sealed partial class ScenarioLoader
|
||||
Id = id,
|
||||
Label = label,
|
||||
Position = [position.X, position.Y, position.Z],
|
||||
StarKind = starProfile.Kind,
|
||||
StarCount = starProfile.StarCount,
|
||||
StarColor = starProfile.StarColor,
|
||||
StarGlow = starProfile.StarGlow,
|
||||
StarSize = starProfile.BaseSize + ((generatedIndex % 4) * 2f),
|
||||
GravityWellRadius = template.GravityWellRadius + ((generatedIndex % 3) * 12f),
|
||||
Stars =
|
||||
[
|
||||
new StarDefinition
|
||||
{
|
||||
Kind = starProfile.Kind,
|
||||
Color = starProfile.StarColor,
|
||||
Glow = starProfile.StarGlow,
|
||||
Size = starProfile.BaseSize + ((generatedIndex % 4) * 2f),
|
||||
},
|
||||
],
|
||||
AsteroidField = new AsteroidFieldDefinition
|
||||
{
|
||||
DecorationCount = template.AsteroidField.DecorationCount + ((generatedIndex % 5) * 10),
|
||||
@@ -181,12 +177,7 @@ public sealed partial class ScenarioLoader
|
||||
Id = definition.Id,
|
||||
Label = definition.Label,
|
||||
Position = definition.Position.ToArray(),
|
||||
StarKind = definition.StarKind,
|
||||
StarCount = definition.StarCount,
|
||||
StarColor = definition.StarColor,
|
||||
StarGlow = definition.StarGlow,
|
||||
StarSize = definition.StarSize,
|
||||
GravityWellRadius = definition.GravityWellRadius,
|
||||
Stars = definition.Stars.Select(s => new StarDefinition { Kind = s.Kind, Color = s.Color, Glow = s.Glow, Size = s.Size, OrbitRadius = s.OrbitRadius, OrbitSpeed = s.OrbitSpeed, OrbitPhaseAtEpoch = s.OrbitPhaseAtEpoch }).ToList(),
|
||||
AsteroidField = new AsteroidFieldDefinition
|
||||
{
|
||||
DecorationCount = definition.AsteroidField.DecorationCount,
|
||||
@@ -214,7 +205,7 @@ public sealed partial class ScenarioLoader
|
||||
Label = planet.Label,
|
||||
PlanetType = planet.PlanetType,
|
||||
Shape = planet.Shape,
|
||||
MoonCount = planet.MoonCount,
|
||||
Moons = planet.Moons.Select(m => new MoonDefinition { Label = m.Label, Size = m.Size, Color = m.Color, OrbitRadius = m.OrbitRadius, OrbitSpeed = m.OrbitSpeed, OrbitPhaseAtEpoch = m.OrbitPhaseAtEpoch, OrbitInclination = m.OrbitInclination, OrbitLongitudeOfAscendingNode = m.OrbitLongitudeOfAscendingNode }).ToList(),
|
||||
OrbitRadius = planet.OrbitRadius,
|
||||
OrbitSpeed = planet.OrbitSpeed,
|
||||
OrbitEccentricity = planet.OrbitEccentricity,
|
||||
@@ -387,14 +378,15 @@ public sealed partial class ScenarioLoader
|
||||
orbitRadius += profile.OrbitGapMin + (Hash01(generatedIndex, 10 + index) * (profile.OrbitGapMax - profile.OrbitGapMin));
|
||||
var orbitEccentricity = 0.01f + (Hash01(generatedIndex, 20 + index) * 0.16f);
|
||||
var orbitInclination = -9f + (Hash01(generatedIndex, 30 + index) * 18f);
|
||||
var moonVariance = (int)MathF.Floor(Hash01(generatedIndex, 40 + index) * 3f);
|
||||
var moonCount = profile.BaseMoonCount + (int)MathF.Floor(Hash01(generatedIndex, 40 + index) * 3f);
|
||||
var planetLabel = $"{BuildPlanetBaseName(generatedIndex, index)}-{index + 1}";
|
||||
|
||||
planets.Add(new PlanetDefinition
|
||||
{
|
||||
Label = $"{BuildPlanetBaseName(generatedIndex, index)}-{index + 1}",
|
||||
Label = planetLabel,
|
||||
PlanetType = profile.Type,
|
||||
Shape = profile.Shape,
|
||||
MoonCount = profile.BaseMoonCount + moonVariance,
|
||||
Moons = GenerateMoons(planetLabel, profile.BaseSize, moonCount),
|
||||
OrbitRadius = orbitRadius,
|
||||
OrbitSpeed = 0.11f / MathF.Sqrt(MathF.Max(orbitRadius * orbitRadius * orbitRadius, 0.02f)),
|
||||
OrbitEccentricity = orbitEccentricity,
|
||||
@@ -471,12 +463,44 @@ public sealed partial class ScenarioLoader
|
||||
return (value & 0x00ffffff) / 16777215f;
|
||||
}
|
||||
|
||||
private static List<MoonDefinition> GenerateMoons(string planetLabel, float planetSize, int moonCount)
|
||||
{
|
||||
var seed = planetLabel.Aggregate(0, (acc, c) => acc * 31 + c);
|
||||
var moons = new List<MoonDefinition>(moonCount);
|
||||
for (var moonIndex = 0; moonIndex < moonCount; moonIndex += 1)
|
||||
{
|
||||
var spacing = planetSize * 1.4f;
|
||||
var radiusVariance = Hash01(seed, 10 + moonIndex) * planetSize * 0.9f;
|
||||
var orbitRadius = (planetSize * 1.8f) + (moonIndex * spacing) + radiusVariance;
|
||||
var orbitSpeed = 0.9f / MathF.Sqrt(MathF.Max(orbitRadius, 1f)) + (moonIndex * 0.003f);
|
||||
var phase = Hash01(seed, 20 + moonIndex) * 360f;
|
||||
var inclination = (Hash01(seed, 30 + moonIndex) - 0.5f) * 28f;
|
||||
var ascendingNode = Hash01(seed, 40 + moonIndex) * 360f;
|
||||
var sizeBase = MathF.Max(2.2f, planetSize * 0.11f);
|
||||
var sizeVariance = Hash01(seed, 50 + moonIndex) * MathF.Max(planetSize * 0.16f, 2.5f);
|
||||
var size = MathF.Min(sizeBase + sizeVariance, planetSize * 0.42f);
|
||||
|
||||
moons.Add(new MoonDefinition
|
||||
{
|
||||
Label = $"{planetLabel}-m{moonIndex + 1}",
|
||||
Size = size,
|
||||
Color = "#c8c4bc",
|
||||
OrbitRadius = orbitRadius,
|
||||
OrbitSpeed = orbitSpeed,
|
||||
OrbitPhaseAtEpoch = phase,
|
||||
OrbitInclination = inclination,
|
||||
OrbitLongitudeOfAscendingNode = ascendingNode,
|
||||
});
|
||||
}
|
||||
|
||||
return moons;
|
||||
}
|
||||
|
||||
private sealed record StarProfile(
|
||||
string Kind,
|
||||
string StarColor,
|
||||
string StarGlow,
|
||||
float BaseSize,
|
||||
int StarCount);
|
||||
float BaseSize);
|
||||
|
||||
private sealed record PlanetProfile(
|
||||
string Type,
|
||||
@@ -490,106 +514,4 @@ public sealed partial class ScenarioLoader
|
||||
public float OrbitGapMax => OrbitGapMin + MathF.Max(0.12f, OrbitGapMin * 0.45f);
|
||||
}
|
||||
|
||||
private static SolarSystemDefinition CreateSolSystem()
|
||||
{
|
||||
var mercuryOrbitAu = 0.3871f;
|
||||
var venusOrbitAu = 0.7233f;
|
||||
var earthOrbitAu = 1.000f;
|
||||
var marsOrbitAu = 1.5237f;
|
||||
var jupiterOrbitAu = 5.203f;
|
||||
var saturnOrbitAu = 9.582f;
|
||||
var uranusOrbitAu = 19.201f;
|
||||
var neptuneOrbitAu = 30.047f;
|
||||
|
||||
return new SolarSystemDefinition
|
||||
{
|
||||
Id = "sol",
|
||||
Label = "Sol",
|
||||
Position = [18.2f, 0.02f, -11.8f],
|
||||
StarKind = "main-sequence",
|
||||
StarCount = 1,
|
||||
StarColor = "#fff1b8",
|
||||
StarGlow = "#ffd35a",
|
||||
StarSize = 696340f,
|
||||
GravityWellRadius = 240f,
|
||||
AsteroidField = new AsteroidFieldDefinition
|
||||
{
|
||||
DecorationCount = 240,
|
||||
RadiusOffset = 422000000f,
|
||||
RadiusVariance = 180000000f,
|
||||
HeightVariance = 22000000f,
|
||||
},
|
||||
ResourceNodes =
|
||||
[
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 0.2f, RadiusOffset = 126000f, InclinationDegrees = 4f, AnchorPlanetIndex = 3, OreAmount = 1000f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 1.8f, RadiusOffset = 148000f, InclinationDegrees = -6f, AnchorPlanetIndex = 3, OreAmount = 1000f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 3.5f, RadiusOffset = 138000f, InclinationDegrees = 8f, AnchorPlanetIndex = 4, OreAmount = 1000f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 5.1f, RadiusOffset = 164000f, InclinationDegrees = -5f, AnchorPlanetIndex = 4, OreAmount = 1000f, ItemId = "ore", ShardCount = 9 },
|
||||
],
|
||||
Planets =
|
||||
[
|
||||
CreateSolPlanet("Mercury", "barren", "sphere", 0, mercuryOrbitAu, 0.2056f, 7.0f, 48f, 29f, 252f, "#b7a08f", 0.03f, false),
|
||||
CreateSolPlanet("Venus", "desert", "sphere", 0, venusOrbitAu, 0.0067f, 3.4f, 76f, 54f, 181f, "#d9b38c", 2.64f, false),
|
||||
CreateSolPlanet("Earth", "terrestrial", "sphere", 1, earthOrbitAu, 0.0167f, 0.0f, 0f, 114f, 100f, "#4f84c4", 0.41f, false),
|
||||
CreateSolPlanet("Mars", "desert", "sphere", 2, marsOrbitAu, 0.0934f, 1.85f, 49f, 286f, 54f, "#c56e52", 0.44f, false),
|
||||
CreateSolPlanet("Jupiter", "gas-giant", "oblate", 95, jupiterOrbitAu, 0.0489f, 1.3f, 100f, 275f, 34f, "#d9b06f", 0.05f, true),
|
||||
CreateSolPlanet("Saturn", "gas-giant", "oblate", 146, saturnOrbitAu, 0.0565f, 2.49f, 113f, 339f, 200f, "#dfc27d", 0.47f, true),
|
||||
CreateSolPlanet("Uranus", "ice-giant", "oblate", 28, uranusOrbitAu, 0.046f, 0.77f, 74f, 97f, 130f, "#9fd3df", 1.71f, true),
|
||||
CreateSolPlanet("Neptune", "ice-giant", "oblate", 16, neptuneOrbitAu, 0.009f, 1.77f, 132f, 273f, 256f, "#4c79c9", 0.49f, true)
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private static PlanetDefinition CreateSolPlanet(
|
||||
string label,
|
||||
string planetType,
|
||||
string shape,
|
||||
int moonCount,
|
||||
float orbitRadiusAu,
|
||||
float orbitEccentricity,
|
||||
float orbitInclination,
|
||||
float ascendingNode,
|
||||
float argumentOfPeriapsis,
|
||||
float phaseAtEpoch,
|
||||
string color,
|
||||
float tilt,
|
||||
bool hasRing)
|
||||
{
|
||||
return new PlanetDefinition
|
||||
{
|
||||
Label = label,
|
||||
PlanetType = planetType,
|
||||
Shape = shape,
|
||||
MoonCount = moonCount,
|
||||
OrbitRadius = orbitRadiusAu,
|
||||
OrbitSpeed = ComputeSolOrbitSpeed(orbitRadiusAu),
|
||||
OrbitEccentricity = orbitEccentricity,
|
||||
OrbitInclination = orbitInclination,
|
||||
OrbitLongitudeOfAscendingNode = ascendingNode,
|
||||
OrbitArgumentOfPeriapsis = argumentOfPeriapsis,
|
||||
OrbitPhaseAtEpoch = phaseAtEpoch,
|
||||
Size = planetType switch
|
||||
{
|
||||
"gas-giant" => label == "Saturn" ? 58232f : 69911f,
|
||||
"ice-giant" => label == "Uranus" ? 25362f : 24622f,
|
||||
_ => label switch
|
||||
{
|
||||
"Mercury" => 2440f,
|
||||
"Venus" => 6052f,
|
||||
"Earth" => 6371f,
|
||||
"Mars" => 3390f,
|
||||
_ => 5000f,
|
||||
},
|
||||
},
|
||||
Color = color,
|
||||
Tilt = tilt,
|
||||
HasRing = hasRing,
|
||||
};
|
||||
}
|
||||
|
||||
private static float ComputeSolOrbitSpeed(float orbitRadiusAu)
|
||||
{
|
||||
const float earthAngularSpeed = 0.11f;
|
||||
return earthAngularSpeed / MathF.Sqrt(orbitRadiusAu * orbitRadiusAu * orbitRadiusAu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,14 +60,14 @@ public sealed partial class ScenarioLoader
|
||||
.ToList();
|
||||
|
||||
var refineries = ownedStations
|
||||
.Where((station) => HasInstalledModules(station, "refinery-stack", "power-core", "liquid-tank"))
|
||||
.Where((station) => HasInstalledModules(station, "module_gen_prod_refinedmetals_01", "module_gen_prod_energycells_01", "module_arg_stor_liquid_m_01"))
|
||||
.ToList();
|
||||
|
||||
if (refineries.Count > 0)
|
||||
{
|
||||
foreach (var refinery in refineries)
|
||||
{
|
||||
refinery.Inventory["refined-metals"] = MathF.Max(GetInventoryAmount(refinery.Inventory, "refined-metals"), MinimumRefineryStock);
|
||||
refinery.Inventory["refinedmetals"] = MathF.Max(GetInventoryAmount(refinery.Inventory, "refinedmetals"), MinimumRefineryStock);
|
||||
}
|
||||
|
||||
if (refineries.All((station) => GetInventoryAmount(station.Inventory, "ore") < MinimumRefineryOre))
|
||||
@@ -76,9 +76,9 @@ public sealed partial class ScenarioLoader
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var shipyard in ownedStations.Where((station) => HasInstalledModules(station, "ship-factory")))
|
||||
foreach (var shipyard in ownedStations.Where((station) => HasInstalledModules(station, "module_gen_build_l_01")))
|
||||
{
|
||||
shipyard.Inventory["refined-metals"] = MathF.Max(GetInventoryAmount(shipyard.Inventory, "refined-metals"), MinimumShipyardStock);
|
||||
shipyard.Inventory["refinedmetals"] = MathF.Max(GetInventoryAmount(shipyard.Inventory, "refinedmetals"), MinimumShipyardStock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,13 +189,13 @@ public sealed partial class ScenarioLoader
|
||||
{
|
||||
foreach (var (moduleId, targetCount) in new (string ModuleId, int TargetCount)[]
|
||||
{
|
||||
("refinery-stack", 1),
|
||||
("container-bay", 1),
|
||||
("fabricator-array", 2),
|
||||
("component-factory", 1),
|
||||
("ship-factory", 1),
|
||||
("solar-array", 2),
|
||||
("dock-bay-small", 2),
|
||||
("module_gen_prod_refinedmetals_01", 1),
|
||||
("module_arg_stor_container_m_01", 1),
|
||||
("module_gen_prod_hullparts_01", 2),
|
||||
("module_gen_prod_advancedelectronics_01", 1),
|
||||
("module_gen_build_l_01", 1),
|
||||
("module_gen_prod_energycells_01", 2),
|
||||
("module_arg_dock_m_01_lowtech", 2),
|
||||
})
|
||||
{
|
||||
if (CountModules(station.InstalledModules, moduleId) < targetCount
|
||||
@@ -210,7 +210,7 @@ public sealed partial class ScenarioLoader
|
||||
|
||||
private static void InitializeStationPopulation(StationRuntime station)
|
||||
{
|
||||
var habitatModules = CountModules(station.InstalledModules, "habitat-ring");
|
||||
var habitatModules = CountModules(station.InstalledModules, "module_arg_hab_m_01");
|
||||
station.PopulationCapacity = 40f + (habitatModules * 220f);
|
||||
station.WorkforceRequired = MathF.Max(12f, station.Modules.Count * 14f);
|
||||
station.Population = habitatModules > 0
|
||||
|
||||
@@ -9,13 +9,18 @@ public sealed partial class ScenarioLoader
|
||||
var celestials = new List<CelestialRuntime>();
|
||||
var lagrangeNodesByPlanetIndex = new Dictionary<int, Dictionary<string, CelestialRuntime>>();
|
||||
|
||||
AddCelestial(
|
||||
celestials,
|
||||
id: $"node-{system.Definition.Id}-star",
|
||||
systemId: system.Definition.Id,
|
||||
kind: SpatialNodeKind.Star,
|
||||
position: Vector3.Zero,
|
||||
localSpaceRadius: MathF.Max(system.Definition.GravityWellRadius + StarBubbleRadiusPadding, LocalSpaceRadius));
|
||||
for (var starIndex = 0; starIndex < system.Definition.Stars.Count; starIndex += 1)
|
||||
{
|
||||
AddCelestial(
|
||||
celestials,
|
||||
id: $"node-{system.Definition.Id}-star-{starIndex + 1}",
|
||||
systemId: system.Definition.Id,
|
||||
kind: SpatialNodeKind.Star,
|
||||
position: Vector3.Zero,
|
||||
localSpaceRadius: LocalSpaceRadius);
|
||||
}
|
||||
|
||||
var primaryStarNodeId = $"node-{system.Definition.Id}-star-1";
|
||||
|
||||
for (var planetIndex = 0; planetIndex < system.Definition.Planets.Count; planetIndex += 1)
|
||||
{
|
||||
@@ -29,7 +34,7 @@ public sealed partial class ScenarioLoader
|
||||
kind: SpatialNodeKind.Planet,
|
||||
position: planetPosition,
|
||||
localSpaceRadius: LocalSpaceRadius,
|
||||
parentNodeId: $"node-{system.Definition.Id}-star");
|
||||
parentNodeId: primaryStarNodeId);
|
||||
|
||||
var lagrangeNodes = new Dictionary<string, CelestialRuntime>(StringComparer.Ordinal);
|
||||
foreach (var point in EnumeratePlanetLagrangePoints(planetPosition, planet))
|
||||
@@ -48,15 +53,10 @@ public sealed partial class ScenarioLoader
|
||||
|
||||
lagrangeNodesByPlanetIndex[planetIndex] = lagrangeNodes;
|
||||
|
||||
if (planet.MoonCount <= 0)
|
||||
for (var moonIndex = 0; moonIndex < planet.Moons.Count; moonIndex += 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var moonOrbitRadius = MathF.Max(planet.Size + 48f, 42f);
|
||||
for (var moonIndex = 0; moonIndex < planet.MoonCount; moonIndex += 1)
|
||||
{
|
||||
var moonPosition = ComputeMoonPosition(planetPosition, moonOrbitRadius, moonIndex, planetIndex);
|
||||
var moon = planet.Moons[moonIndex];
|
||||
var moonPosition = ComputeMoonPosition(planetPosition, moon);
|
||||
AddCelestial(
|
||||
celestials,
|
||||
id: $"node-{system.Definition.Id}-planet-{planetIndex + 1}-moon-{moonIndex + 1}",
|
||||
@@ -65,7 +65,6 @@ public sealed partial class ScenarioLoader
|
||||
position: moonPosition,
|
||||
localSpaceRadius: LocalSpaceRadius,
|
||||
parentNodeId: planetCelestial.Id);
|
||||
moonOrbitRadius += 30f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,10 +231,11 @@ public sealed partial class ScenarioLoader
|
||||
return new Vector3(x, 0f, z);
|
||||
}
|
||||
|
||||
private static Vector3 ComputeMoonPosition(Vector3 planetPosition, float orbitRadius, int moonIndex, int planetIndex)
|
||||
private static Vector3 ComputeMoonPosition(Vector3 planetPosition, MoonDefinition moon)
|
||||
{
|
||||
var angle = ((MathF.PI * 2f) / MathF.Max(1, moonIndex + 3)) * (moonIndex + 1) + (planetIndex * 0.37f);
|
||||
return Add(planetPosition, new Vector3(MathF.Cos(angle) * orbitRadius, 0f, MathF.Sin(angle) * orbitRadius));
|
||||
var angle = DegreesToRadians(moon.OrbitPhaseAtEpoch);
|
||||
var local = new Vector3(MathF.Cos(angle) * moon.OrbitRadius, 0f, MathF.Sin(angle) * moon.OrbitRadius);
|
||||
return Add(planetPosition, local);
|
||||
}
|
||||
|
||||
private static ShipSpatialStateRuntime CreateInitialShipSpatialState(string systemId, Vector3 position, IReadOnlyCollection<CelestialRuntime> celestials)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -141,9 +141,9 @@ public sealed partial class SimulationEngine
|
||||
string.Equals(s.Definition.Kind, "construction", StringComparison.Ordinal)),
|
||||
ControlledSystemCount = GetFactionControlledSystemsCount(world, factionId),
|
||||
TargetSystemCount = Math.Max(1, Math.Min(StrategicControlTargetSystems, world.Systems.Count)),
|
||||
HasShipFactory = stations.Any(s => s.InstalledModules.Contains("ship-factory", StringComparer.Ordinal)),
|
||||
HasShipFactory = stations.Any(s => s.InstalledModules.Contains("module_gen_build_l_01", StringComparer.Ordinal)),
|
||||
OreStockpile = stations.Sum(s => GetInventoryAmount(s.Inventory, "ore")),
|
||||
RefinedMetalsStockpile = stations.Sum(s => GetInventoryAmount(s.Inventory, "refined-metals")),
|
||||
RefinedMetalsStockpile = stations.Sum(s => GetInventoryAmount(s.Inventory, "refinedmetals")),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,37 +24,18 @@ public sealed partial class SimulationEngine
|
||||
return local;
|
||||
}
|
||||
|
||||
private static Vector3 ComputeMoonOffset(PlanetDefinition planet, int moonIndex, float timeSeconds)
|
||||
private static Vector3 ComputeMoonOffset(MoonDefinition moon, float timeSeconds)
|
||||
{
|
||||
var orbitRadius = ComputeMoonOrbitRadius(planet, moonIndex);
|
||||
var speed = ComputeMoonOrbitSpeed(planet, moonIndex);
|
||||
var phase = HashUnit($"{planet.Label}:{moonIndex}:phase") * MathF.PI * 2f;
|
||||
var inclination = DegreesToRadians((HashUnit($"{planet.Label}:{moonIndex}:inclination") - 0.5f) * 28f);
|
||||
var ascendingNode = DegreesToRadians(HashUnit($"{planet.Label}:{moonIndex}:node") * 360f);
|
||||
var angle = phase + (timeSeconds * speed);
|
||||
|
||||
var angle = DegreesToRadians(moon.OrbitPhaseAtEpoch) + (timeSeconds * moon.OrbitSpeed);
|
||||
var local = new Vector3(
|
||||
MathF.Cos(angle) * orbitRadius,
|
||||
MathF.Cos(angle) * moon.OrbitRadius,
|
||||
0f,
|
||||
MathF.Sin(angle) * orbitRadius);
|
||||
local = RotateAroundX(local, inclination);
|
||||
local = RotateAroundY(local, ascendingNode);
|
||||
MathF.Sin(angle) * moon.OrbitRadius);
|
||||
local = RotateAroundX(local, DegreesToRadians(moon.OrbitInclination));
|
||||
local = RotateAroundY(local, DegreesToRadians(moon.OrbitLongitudeOfAscendingNode));
|
||||
return local;
|
||||
}
|
||||
|
||||
private static float ComputeMoonOrbitRadius(PlanetDefinition planet, int moonIndex)
|
||||
{
|
||||
var spacing = planet.Size * 1.4f;
|
||||
var variance = HashUnit($"{planet.Label}:{moonIndex}:radius") * planet.Size * 0.9f;
|
||||
return (planet.Size * 1.8f) + (moonIndex * spacing) + variance;
|
||||
}
|
||||
|
||||
private static float ComputeMoonOrbitSpeed(PlanetDefinition planet, int moonIndex)
|
||||
{
|
||||
var radius = ComputeMoonOrbitRadius(planet, moonIndex);
|
||||
return 0.9f / MathF.Sqrt(MathF.Max(radius, 1f)) + (moonIndex * 0.003f);
|
||||
}
|
||||
|
||||
private static float ComputeResourceNodeOrbitSpeed(ResourceNodeRuntime node)
|
||||
{
|
||||
var baseSpeed = 0.24f;
|
||||
@@ -179,10 +160,24 @@ public sealed partial class SimulationEngine
|
||||
|
||||
foreach (var system in world.Systems)
|
||||
{
|
||||
var starNodeId = $"node-{system.Definition.Id}-star";
|
||||
if (celestialsById.TryGetValue(starNodeId, out var starNode))
|
||||
for (var starIndex = 0; starIndex < system.Definition.Stars.Count; starIndex += 1)
|
||||
{
|
||||
starNode.Position = Vector3.Zero;
|
||||
var star = system.Definition.Stars[starIndex];
|
||||
var starNodeId = $"node-{system.Definition.Id}-star-{starIndex + 1}";
|
||||
if (!celestialsById.TryGetValue(starNodeId, out var starNode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (star.OrbitRadius <= 0f)
|
||||
{
|
||||
starNode.Position = Vector3.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
var angle = DegreesToRadians(star.OrbitPhaseAtEpoch) + (worldTimeSeconds * star.OrbitSpeed);
|
||||
starNode.Position = new Vector3(MathF.Cos(angle) * star.OrbitRadius, 0f, MathF.Sin(angle) * star.OrbitRadius);
|
||||
}
|
||||
}
|
||||
|
||||
for (var planetIndex = 0; planetIndex < system.Definition.Planets.Count; planetIndex += 1)
|
||||
@@ -206,7 +201,7 @@ public sealed partial class SimulationEngine
|
||||
}
|
||||
}
|
||||
|
||||
for (var moonIndex = 0; moonIndex < planet.MoonCount; moonIndex += 1)
|
||||
for (var moonIndex = 0; moonIndex < planet.Moons.Count; moonIndex += 1)
|
||||
{
|
||||
var moonId = $"node-{system.Definition.Id}-planet-{planetIndex + 1}-moon-{moonIndex + 1}";
|
||||
if (!celestialsById.TryGetValue(moonId, out var moonNode))
|
||||
@@ -214,7 +209,7 @@ public sealed partial class SimulationEngine
|
||||
continue;
|
||||
}
|
||||
|
||||
moonNode.Position = Add(planetPosition, ComputeMoonOffset(planet, moonIndex, worldTimeSeconds));
|
||||
moonNode.Position = Add(planetPosition, ComputeMoonOffset(planet.Moons[moonIndex], worldTimeSeconds));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,9 @@ public sealed partial class SimulationEngine
|
||||
_ => 0f,
|
||||
};
|
||||
|
||||
var bulkBays = CountStationModules(station, "bulk-bay");
|
||||
var liquidTanks = CountStationModules(station, "liquid-tank");
|
||||
var containerBays = CountStationModules(station, "container-bay");
|
||||
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
|
||||
{
|
||||
@@ -118,8 +118,8 @@ public sealed partial class SimulationEngine
|
||||
private static string? GetStorageRequirement(string storageClass) =>
|
||||
storageClass switch
|
||||
{
|
||||
"solid" => "bulk-bay",
|
||||
"liquid" => "liquid-tank",
|
||||
"solid" => "module_arg_stor_solid_m_01",
|
||||
"liquid" => "module_arg_stor_liquid_m_01",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
|
||||
@@ -20,15 +20,27 @@ public sealed partial class SimulationEngine
|
||||
system.Definition.Id,
|
||||
system.Definition.Label,
|
||||
ToDto(system.Position),
|
||||
system.Definition.StarKind,
|
||||
system.Definition.StarCount,
|
||||
system.Definition.StarColor,
|
||||
system.Definition.StarSize,
|
||||
system.Definition.Stars.Select(star => new StarSnapshot(
|
||||
star.Kind,
|
||||
star.Color,
|
||||
star.Glow,
|
||||
star.Size,
|
||||
star.OrbitRadius,
|
||||
star.OrbitSpeed,
|
||||
star.OrbitPhaseAtEpoch)).ToList(),
|
||||
system.Definition.Planets.Select(planet => new PlanetSnapshot(
|
||||
planet.Label,
|
||||
planet.PlanetType,
|
||||
planet.Shape,
|
||||
planet.MoonCount,
|
||||
planet.Moons.Select(moon => new MoonSnapshot(
|
||||
moon.Label,
|
||||
moon.Size,
|
||||
moon.Color,
|
||||
moon.OrbitRadius,
|
||||
moon.OrbitSpeed,
|
||||
moon.OrbitPhaseAtEpoch,
|
||||
moon.OrbitInclination,
|
||||
moon.OrbitLongitudeOfAscendingNode)).ToList(),
|
||||
planet.OrbitRadius,
|
||||
planet.OrbitSpeed,
|
||||
planet.OrbitEccentricity,
|
||||
|
||||
@@ -110,9 +110,9 @@ public sealed partial class SimulationEngine
|
||||
const float StorageExpansionThreshold = 0.85f;
|
||||
var storageExpansionCandidates = new[]
|
||||
{
|
||||
("solid", "bulk-bay"),
|
||||
("liquid", "liquid-tank"),
|
||||
("container", "container-bay"),
|
||||
("solid", "module_arg_stor_solid_m_01"),
|
||||
("liquid", "module_arg_stor_liquid_m_01"),
|
||||
("container", "module_arg_stor_container_m_01"),
|
||||
};
|
||||
|
||||
foreach (var (storageClass, moduleId) in storageExpansionCandidates)
|
||||
@@ -136,25 +136,25 @@ public sealed partial class SimulationEngine
|
||||
var priorities = GetFactionExpansionPressure(world, station.FactionId) > 0f
|
||||
? new (string ModuleId, int TargetCount)[]
|
||||
{
|
||||
("refinery-stack", 1),
|
||||
("bulk-bay", 1),
|
||||
("container-bay", 1),
|
||||
("fabricator-array", 2),
|
||||
("component-factory", 1),
|
||||
("ship-factory", 1),
|
||||
("dock-bay-small", 2),
|
||||
("solar-array", 2),
|
||||
("module_gen_prod_refinedmetals_01", 1),
|
||||
("module_arg_stor_solid_m_01", 1),
|
||||
("module_arg_stor_container_m_01", 1),
|
||||
("module_gen_prod_hullparts_01", 2),
|
||||
("module_gen_prod_advancedelectronics_01", 1),
|
||||
("module_gen_build_l_01", 1),
|
||||
("module_arg_dock_m_01_lowtech", 2),
|
||||
("module_gen_prod_energycells_01", 2),
|
||||
}
|
||||
: new (string ModuleId, int TargetCount)[]
|
||||
{
|
||||
("refinery-stack", 1),
|
||||
("bulk-bay", 1),
|
||||
("container-bay", 1),
|
||||
("fabricator-array", 2),
|
||||
("component-factory", 1),
|
||||
("ship-factory", 1),
|
||||
("solar-array", 2),
|
||||
("dock-bay-small", 2),
|
||||
("module_gen_prod_refinedmetals_01", 1),
|
||||
("module_arg_stor_solid_m_01", 1),
|
||||
("module_arg_stor_container_m_01", 1),
|
||||
("module_gen_prod_hullparts_01", 2),
|
||||
("module_gen_prod_advancedelectronics_01", 1),
|
||||
("module_gen_build_l_01", 1),
|
||||
("module_gen_prod_energycells_01", 2),
|
||||
("module_arg_dock_m_01_lowtech", 2),
|
||||
};
|
||||
|
||||
foreach (var (moduleId, targetCount) in priorities)
|
||||
@@ -225,7 +225,7 @@ public sealed partial class SimulationEngine
|
||||
}
|
||||
|
||||
private static int GetDockingPadCount(StationRuntime station) =>
|
||||
CountModules(station.InstalledModules, "dock-bay-small") * 2;
|
||||
CountModules(station.InstalledModules, "module_arg_dock_m_01_lowtech") * 2;
|
||||
|
||||
private static int? ReserveDockingPad(StationRuntime station, string shipId)
|
||||
{
|
||||
|
||||
@@ -14,22 +14,22 @@ public sealed partial class SimulationEngine
|
||||
|
||||
var desiredOrders = new List<DesiredMarketOrder>();
|
||||
var waterReserve = MathF.Max(30f, station.Population * 3f);
|
||||
var refinedReserve = HasStationModules(station, "fabricator-array") ? 140f : 40f;
|
||||
var refinedReserve = HasStationModules(station, "module_gen_prod_hullparts_01") ? 140f : 40f;
|
||||
var oreReserve = HasRefineryCapability(station) ? 180f : 0f;
|
||||
var shipPartsReserve = HasStationModules(station, "fabricator-array")
|
||||
&& !HasStationModules(station, "component-factory", "ship-factory")
|
||||
var shipPartsReserve = HasStationModules(station, "module_gen_prod_hullparts_01")
|
||||
&& !HasStationModules(station, "module_gen_prod_advancedelectronics_01", "module_gen_build_l_01")
|
||||
&& FactionCommanderHasDirective(world, station.FactionId, "produce-military-ships")
|
||||
? 90f
|
||||
: 0f;
|
||||
|
||||
AddDemandOrder(desiredOrders, station, "water", waterReserve, valuationBase: 1.1f);
|
||||
AddDemandOrder(desiredOrders, station, "ore", oreReserve, valuationBase: 1.0f);
|
||||
AddDemandOrder(desiredOrders, station, "refined-metals", refinedReserve, valuationBase: 1.15f);
|
||||
AddDemandOrder(desiredOrders, station, "ship-parts", shipPartsReserve, valuationBase: 1.3f);
|
||||
AddDemandOrder(desiredOrders, station, "refinedmetals", refinedReserve, valuationBase: 1.15f);
|
||||
AddDemandOrder(desiredOrders, station, "hullparts", shipPartsReserve, valuationBase: 1.3f);
|
||||
|
||||
AddSupplyOrder(desiredOrders, station, "water", waterReserve * 1.5f, reserveFloor: waterReserve, valuationBase: 0.65f);
|
||||
AddSupplyOrder(desiredOrders, station, "ore", oreReserve * 1.4f, reserveFloor: oreReserve, valuationBase: 0.7f);
|
||||
AddSupplyOrder(desiredOrders, station, "refined-metals", refinedReserve * 1.4f, reserveFloor: refinedReserve, valuationBase: 0.95f);
|
||||
AddSupplyOrder(desiredOrders, station, "refinedmetals", refinedReserve * 1.4f, reserveFloor: refinedReserve, valuationBase: 0.95f);
|
||||
|
||||
ReconcileStationMarketOrders(world, station, desiredOrders);
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public sealed partial class SimulationEngine
|
||||
var fleetPressure = FactionCommanderHasDirective(world, station.FactionId, "produce-military-ships") ? 1f : 0f;
|
||||
priority += recipe.Id switch
|
||||
{
|
||||
"ship-parts-integration" => HasStationModules(station, "component-factory", "ship-factory")
|
||||
"ship-parts-integration" => HasStationModules(station, "module_gen_prod_advancedelectronics_01", "module_gen_build_l_01")
|
||||
? -140f * MathF.Max(expansionPressure, fleetPressure)
|
||||
: 280f * MathF.Max(expansionPressure, fleetPressure),
|
||||
"hull-fabrication" => 180f * expansionPressure,
|
||||
@@ -211,7 +211,7 @@ public sealed partial class SimulationEngine
|
||||
}
|
||||
|
||||
private static bool HasRefineryCapability(StationRuntime station) =>
|
||||
HasStationModules(station, "refinery-stack", "power-core", "bulk-bay");
|
||||
HasStationModules(station, "module_gen_prod_refinedmetals_01", "module_gen_prod_energycells_01", "module_arg_stor_solid_m_01");
|
||||
|
||||
private static void AddDemandOrder(ICollection<DesiredMarketOrder> desiredOrders, StationRuntime station, string itemId, float targetAmount, float valuationBase)
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ public sealed partial class SimulationEngine
|
||||
var requiredWater = station.Population * WaterConsumptionPerWorkerPerSecond * deltaSeconds;
|
||||
var consumedWater = RemoveInventory(station.Inventory, "water", requiredWater);
|
||||
var waterSatisfied = requiredWater <= 0.01f || consumedWater + 0.001f >= requiredWater;
|
||||
var habitatModules = CountModules(station.InstalledModules, "habitat-ring");
|
||||
var habitatModules = CountModules(station.InstalledModules, "module_arg_hab_m_01");
|
||||
station.PopulationCapacity = 40f + (habitatModules * 220f);
|
||||
|
||||
if (waterSatisfied)
|
||||
|
||||
@@ -4,5 +4,4 @@ public sealed class WorldGenerationOptions
|
||||
{
|
||||
public int TargetSystemCount { get; init; } = 160;
|
||||
|
||||
public bool IncludeSolSystem { get; init; } = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user