feat: production chain
This commit is contained in:
@@ -4,13 +4,18 @@ namespace SpaceGame.Simulation.Api.Simulation;
|
||||
|
||||
public sealed partial class ScenarioLoader
|
||||
{
|
||||
private static List<SolarSystemDefinition> InjectSpecialSystems(IReadOnlyList<SolarSystemDefinition> authoredSystems)
|
||||
private const string SolSystemId = "sol";
|
||||
private const string DevelopmentCompanionSystemId = "helios";
|
||||
|
||||
private static List<SolarSystemDefinition> InjectSpecialSystems(
|
||||
IReadOnlyList<SolarSystemDefinition> authoredSystems,
|
||||
bool includeSolSystem)
|
||||
{
|
||||
var systems = authoredSystems
|
||||
.Select(CloneSystemDefinition)
|
||||
.ToList();
|
||||
|
||||
if (systems.All((system) => system.Id != "sol"))
|
||||
if (includeSolSystem && systems.All((system) => system.Id != "sol"))
|
||||
{
|
||||
systems.Add(CreateSolSystem());
|
||||
}
|
||||
@@ -18,13 +23,25 @@ public sealed partial class ScenarioLoader
|
||||
return systems;
|
||||
}
|
||||
|
||||
private static List<SolarSystemDefinition> ExpandSystems(IReadOnlyList<SolarSystemDefinition> authoredSystems)
|
||||
private static List<SolarSystemDefinition> ExpandSystems(
|
||||
IReadOnlyList<SolarSystemDefinition> authoredSystems,
|
||||
int targetSystemCount)
|
||||
{
|
||||
var systems = authoredSystems
|
||||
.Select(CloneSystemDefinition)
|
||||
.ToList();
|
||||
|
||||
if (systems.Count >= TargetSystemCount || authoredSystems.Count == 0)
|
||||
if (targetSystemCount <= 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
if (systems.Count > targetSystemCount)
|
||||
{
|
||||
return TrimSystemsToTarget(systems, targetSystemCount);
|
||||
}
|
||||
|
||||
if (systems.Count >= targetSystemCount || authoredSystems.Count == 0)
|
||||
{
|
||||
return systems;
|
||||
}
|
||||
@@ -32,9 +49,11 @@ public sealed partial class ScenarioLoader
|
||||
var existingIds = systems
|
||||
.Select((system) => system.Id)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
var generatedPositions = BuildGalaxyPositions(authoredSystems.Select((system) => ToVector(system.Position)).ToList(), TargetSystemCount - systems.Count);
|
||||
var generatedPositions = BuildGalaxyPositions(
|
||||
authoredSystems.Select((system) => ToVector(system.Position)).ToList(),
|
||||
targetSystemCount - systems.Count);
|
||||
|
||||
for (var index = systems.Count; index < TargetSystemCount; index += 1)
|
||||
for (var index = systems.Count; index < targetSystemCount; index += 1)
|
||||
{
|
||||
var template = authoredSystems[index % authoredSystems.Count];
|
||||
var name = GeneratedSystemNames[(index - authoredSystems.Count) % GeneratedSystemNames.Length];
|
||||
@@ -50,6 +69,63 @@ public sealed partial class ScenarioLoader
|
||||
return systems;
|
||||
}
|
||||
|
||||
private static List<SolarSystemDefinition> TrimSystemsToTarget(
|
||||
IReadOnlyList<SolarSystemDefinition> systems,
|
||||
int targetSystemCount)
|
||||
{
|
||||
var selected = new List<SolarSystemDefinition>(targetSystemCount);
|
||||
|
||||
void AddById(string systemId)
|
||||
{
|
||||
var system = systems.FirstOrDefault((candidate) => string.Equals(candidate.Id, systemId, StringComparison.Ordinal));
|
||||
if (system is not null && selected.All((candidate) => !string.Equals(candidate.Id, systemId, StringComparison.Ordinal)))
|
||||
{
|
||||
selected.Add(system);
|
||||
}
|
||||
}
|
||||
|
||||
AddById(SolSystemId);
|
||||
AddById(DevelopmentCompanionSystemId);
|
||||
|
||||
foreach (var system in systems)
|
||||
{
|
||||
if (selected.Count >= targetSystemCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (selected.Any((candidate) => string.Equals(candidate.Id, system.Id, StringComparison.Ordinal)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
selected.Add(system);
|
||||
}
|
||||
|
||||
if (selected.Count > 0 && selected.Count <= 4)
|
||||
{
|
||||
ApplyCompactGalaxyLayout(selected);
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
private static void ApplyCompactGalaxyLayout(IReadOnlyList<SolarSystemDefinition> systems)
|
||||
{
|
||||
var compactPositions = new[]
|
||||
{
|
||||
new[] { 0f, 0f, 0f },
|
||||
new[] { 2600f, 24f, -420f },
|
||||
new[] { -2400f, -36f, 560f },
|
||||
new[] { 520f, 42f, 2480f },
|
||||
};
|
||||
|
||||
for (var index = 0; index < systems.Count && index < compactPositions.Length; index += 1)
|
||||
{
|
||||
systems[index].Position = compactPositions[index];
|
||||
}
|
||||
}
|
||||
|
||||
private static SolarSystemDefinition CreateGeneratedSystem(
|
||||
SolarSystemDefinition template,
|
||||
string label,
|
||||
@@ -66,6 +142,9 @@ public sealed partial class ScenarioLoader
|
||||
SourceKind = node.SourceKind,
|
||||
Angle = node.Angle,
|
||||
RadiusOffset = node.RadiusOffset,
|
||||
InclinationDegrees = node.InclinationDegrees,
|
||||
AnchorPlanetIndex = node.AnchorPlanetIndex,
|
||||
AnchorMoonIndex = node.AnchorMoonIndex,
|
||||
OreAmount = node.OreAmount,
|
||||
ItemId = node.ItemId,
|
||||
ShardCount = node.ShardCount,
|
||||
@@ -118,8 +197,12 @@ public sealed partial class ScenarioLoader
|
||||
ResourceNodes = definition.ResourceNodes
|
||||
.Select((node) => new ResourceNodeDefinition
|
||||
{
|
||||
SourceKind = node.SourceKind,
|
||||
Angle = node.Angle,
|
||||
RadiusOffset = node.RadiusOffset,
|
||||
InclinationDegrees = node.InclinationDegrees,
|
||||
AnchorPlanetIndex = node.AnchorPlanetIndex,
|
||||
AnchorMoonIndex = node.AnchorMoonIndex,
|
||||
OreAmount = node.OreAmount,
|
||||
ItemId = node.ItemId,
|
||||
ShardCount = node.ShardCount,
|
||||
@@ -161,6 +244,9 @@ public sealed partial class ScenarioLoader
|
||||
SourceKind = node.SourceKind,
|
||||
Angle = node.Angle,
|
||||
RadiusOffset = node.RadiusOffset,
|
||||
InclinationDegrees = node.InclinationDegrees,
|
||||
AnchorPlanetIndex = node.AnchorPlanetIndex,
|
||||
AnchorMoonIndex = node.AnchorMoonIndex,
|
||||
OreAmount = node.OreAmount,
|
||||
ItemId = node.ItemId,
|
||||
ShardCount = node.ShardCount,
|
||||
@@ -239,9 +325,8 @@ public sealed partial class ScenarioLoader
|
||||
|
||||
private static IEnumerable<ResourceNodeDefinition> BuildAsteroidBeltNodes(int generatedIndex, IReadOnlyList<PlanetDefinition> planets)
|
||||
{
|
||||
var beltRadius = ResolveAsteroidBeltRadius(planets, generatedIndex);
|
||||
var nodeCount = 4 + (generatedIndex % 4);
|
||||
var oreAmount = 2800f + ((generatedIndex % 5) * 320f);
|
||||
var oreAmount = 1000f;
|
||||
|
||||
for (var index = 0; index < nodeCount; index += 1)
|
||||
{
|
||||
@@ -249,7 +334,9 @@ public sealed partial class ScenarioLoader
|
||||
{
|
||||
SourceKind = "asteroid-belt",
|
||||
Angle = ((MathF.PI * 2f) / nodeCount) * index + Jitter(generatedIndex, 180 + index, 0.22f),
|
||||
RadiusOffset = beltRadius + Jitter(generatedIndex, 200 + index, 80f),
|
||||
RadiusOffset = 120f + Jitter(generatedIndex, 200 + index, 36f),
|
||||
InclinationDegrees = Jitter(generatedIndex, 280 + index, 12f),
|
||||
AnchorPlanetIndex = ResolveAsteroidAnchorPlanetIndex(planets),
|
||||
OreAmount = oreAmount,
|
||||
ItemId = "ore",
|
||||
ShardCount = 6 + (index % 4),
|
||||
@@ -269,15 +356,27 @@ public sealed partial class ScenarioLoader
|
||||
yield break;
|
||||
}
|
||||
|
||||
var gasAnchorIndex = 0;
|
||||
for (var index = 0; index < planets.Count; index += 1)
|
||||
{
|
||||
if (ReferenceEquals(planets[index], gasAnchor))
|
||||
{
|
||||
gasAnchorIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var nodeCount = 2 + (generatedIndex % 3);
|
||||
var gasAmount = 2200f + ((generatedIndex % 4) * 260f);
|
||||
var gasAmount = 1000f;
|
||||
for (var index = 0; index < nodeCount; index += 1)
|
||||
{
|
||||
yield return new ResourceNodeDefinition
|
||||
{
|
||||
SourceKind = "gas-cloud",
|
||||
Angle = gasAnchor.OrbitPhaseAtEpoch * (MathF.PI / 180f) + (((MathF.PI * 2f) / nodeCount) * index) + Jitter(generatedIndex, 240 + index, 0.18f),
|
||||
RadiusOffset = gasAnchor.OrbitRadius + 90f + Jitter(generatedIndex, 260 + index, 70f),
|
||||
RadiusOffset = 170f + Jitter(generatedIndex, 260 + index, 44f),
|
||||
InclinationDegrees = Jitter(generatedIndex, 320 + index, 10f),
|
||||
AnchorPlanetIndex = gasAnchorIndex,
|
||||
OreAmount = gasAmount,
|
||||
ItemId = "gas",
|
||||
ShardCount = 10 + index,
|
||||
@@ -285,19 +384,29 @@ public sealed partial class ScenarioLoader
|
||||
}
|
||||
}
|
||||
|
||||
private static float ResolveAsteroidBeltRadius(IReadOnlyList<PlanetDefinition> planets, int generatedIndex)
|
||||
private static int ResolveAsteroidAnchorPlanetIndex(IReadOnlyList<PlanetDefinition> planets)
|
||||
{
|
||||
var gap = planets
|
||||
.Zip(planets.Skip(1), (left, right) => (LeftOrbitRadius: left.OrbitRadius, RightOrbitRadius: right.OrbitRadius, Gap: right.OrbitRadius - left.OrbitRadius))
|
||||
.OrderByDescending((entry) => entry.Gap)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (gap.Gap > 1f)
|
||||
if (planets.Count == 0)
|
||||
{
|
||||
return gap.LeftOrbitRadius + (gap.Gap * 0.52f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 420f + ((generatedIndex % 5) * 60f);
|
||||
var gasGiantIndex = -1;
|
||||
for (var index = 0; index < planets.Count; index += 1)
|
||||
{
|
||||
if (planets[index].PlanetType is "gas-giant" or "ice-giant")
|
||||
{
|
||||
gasGiantIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gasGiantIndex > 0)
|
||||
{
|
||||
return gasGiantIndex - 1;
|
||||
}
|
||||
|
||||
return Math.Clamp((planets.Count / 2) - 1, 0, planets.Count - 1);
|
||||
}
|
||||
|
||||
private static List<PlanetDefinition> BuildGeneratedPlanets(
|
||||
@@ -424,6 +533,15 @@ public sealed partial class ScenarioLoader
|
||||
|
||||
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",
|
||||
@@ -438,30 +556,30 @@ public sealed partial class ScenarioLoader
|
||||
AsteroidField = new AsteroidFieldDefinition
|
||||
{
|
||||
DecorationCount = 240,
|
||||
RadiusOffset = 780f,
|
||||
RadiusOffset = ScaleSolOrbitRadiusFromAu(2.82f),
|
||||
RadiusVariance = 180f,
|
||||
HeightVariance = 22f,
|
||||
},
|
||||
ResourceNodes =
|
||||
[
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 0.2f, RadiusOffset = 720f, OreAmount = 4200f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 1.8f, RadiusOffset = 760f, OreAmount = 4200f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 3.5f, RadiusOffset = 810f, OreAmount = 4200f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 5.1f, RadiusOffset = 780f, OreAmount = 4200f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "gas-cloud", Angle = 0.9f, RadiusOffset = 1650f, OreAmount = 2800f, ItemId = "gas", ShardCount = 12 },
|
||||
new ResourceNodeDefinition { SourceKind = "gas-cloud", Angle = 2.7f, RadiusOffset = 1710f, OreAmount = 2800f, ItemId = "gas", ShardCount = 12 },
|
||||
new ResourceNodeDefinition { SourceKind = "gas-cloud", Angle = 4.8f, RadiusOffset = 2140f, OreAmount = 2600f, ItemId = "gas", ShardCount = 10 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 0.2f, RadiusOffset = 126f, InclinationDegrees = 4f, AnchorPlanetIndex = 3, OreAmount = 1000f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 1.8f, RadiusOffset = 148f, InclinationDegrees = -6f, AnchorPlanetIndex = 3, OreAmount = 1000f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 3.5f, RadiusOffset = 138f, InclinationDegrees = 8f, AnchorPlanetIndex = 4, OreAmount = 1000f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "asteroid-belt", Angle = 5.1f, RadiusOffset = 164f, InclinationDegrees = -5f, AnchorPlanetIndex = 4, OreAmount = 1000f, ItemId = "ore", ShardCount = 9 },
|
||||
new ResourceNodeDefinition { SourceKind = "gas-cloud", Angle = 0.9f, RadiusOffset = 210f, InclinationDegrees = 3f, AnchorPlanetIndex = 4, OreAmount = 1000f, ItemId = "gas", ShardCount = 12 },
|
||||
new ResourceNodeDefinition { SourceKind = "gas-cloud", Angle = 2.7f, RadiusOffset = 228f, InclinationDegrees = -4f, AnchorPlanetIndex = 5, OreAmount = 1000f, ItemId = "gas", ShardCount = 12 },
|
||||
new ResourceNodeDefinition { SourceKind = "gas-cloud", Angle = 4.8f, RadiusOffset = 186f, InclinationDegrees = 6f, AnchorPlanetIndex = 6, OreAmount = 1000f, ItemId = "gas", ShardCount = 10 },
|
||||
],
|
||||
Planets =
|
||||
[
|
||||
CreateSolPlanet("Mercury", "barren", "sphere", 0, 180f, 0.19f, 0.2056f, 7.0f, 48f, 29f, 252f, "#b7a08f", 0.03f, false),
|
||||
CreateSolPlanet("Venus", "desert", "sphere", 0, 270f, 0.14f, 0.0067f, 3.4f, 76f, 54f, 181f, "#d9b38c", 2.64f, false),
|
||||
CreateSolPlanet("Earth", "terrestrial", "sphere", 1, 380f, 0.11f, 0.0167f, 0.0f, 0f, 114f, 100f, "#4f84c4", 0.41f, false),
|
||||
CreateSolPlanet("Mars", "desert", "sphere", 2, 500f, 0.09f, 0.0934f, 1.85f, 49f, 286f, 54f, "#c56e52", 0.44f, false),
|
||||
CreateSolPlanet("Jupiter", "gas-giant", "oblate", 95, 980f, 0.05f, 0.0489f, 1.3f, 100f, 275f, 34f, "#d9b06f", 0.05f, true),
|
||||
CreateSolPlanet("Saturn", "gas-giant", "oblate", 146, 1380f, 0.035f, 0.0565f, 2.49f, 113f, 339f, 200f, "#dfc27d", 0.47f, true),
|
||||
CreateSolPlanet("Uranus", "ice-giant", "oblate", 28, 1760f, 0.026f, 0.046f, 0.77f, 74f, 97f, 130f, "#9fd3df", 1.71f, true),
|
||||
CreateSolPlanet("Neptune", "ice-giant", "oblate", 16, 2140f, 0.021f, 0.009f, 1.77f, 132f, 273f, 256f, "#4c79c9", 0.49f, true)
|
||||
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)
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -471,8 +589,7 @@ public sealed partial class ScenarioLoader
|
||||
string planetType,
|
||||
string shape,
|
||||
int moonCount,
|
||||
float orbitRadius,
|
||||
float orbitSpeed,
|
||||
float orbitRadiusAu,
|
||||
float orbitEccentricity,
|
||||
float orbitInclination,
|
||||
float ascendingNode,
|
||||
@@ -488,8 +605,8 @@ public sealed partial class ScenarioLoader
|
||||
PlanetType = planetType,
|
||||
Shape = shape,
|
||||
MoonCount = moonCount,
|
||||
OrbitRadius = orbitRadius,
|
||||
OrbitSpeed = orbitSpeed,
|
||||
OrbitRadius = ScaleSolOrbitRadiusFromAu(orbitRadiusAu),
|
||||
OrbitSpeed = ComputeSolOrbitSpeed(orbitRadiusAu),
|
||||
OrbitEccentricity = orbitEccentricity,
|
||||
OrbitInclination = orbitInclination,
|
||||
OrbitLongitudeOfAscendingNode = ascendingNode,
|
||||
@@ -506,4 +623,13 @@ public sealed partial class ScenarioLoader
|
||||
HasRing = hasRing,
|
||||
};
|
||||
}
|
||||
|
||||
private static float ScaleSolOrbitRadiusFromAu(float orbitRadiusAu) =>
|
||||
MathF.Round(500f * MathF.Pow(orbitRadiusAu, 0.70f));
|
||||
|
||||
private static float ComputeSolOrbitSpeed(float orbitRadiusAu)
|
||||
{
|
||||
const float earthAngularSpeed = 0.11f;
|
||||
return earthAngularSpeed / MathF.Sqrt(orbitRadiusAu * orbitRadiusAu * orbitRadiusAu);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user