Replace arbitrary game units with real-world measurements throughout the simulation and viewer: planet orbits in AU, sizes in km, galaxy positions in light-years. Add SimulationUnits helpers for conversions, separate WarpSpeed from FtlSpeed for ships, fix FTL transit progress to use galaxy-space distances, overhaul Lagrange point placement with Hill sphere approximation, and update the viewer to scale and format all distances correctly. Ships in FTL transit now render in galaxy view. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
640 lines
22 KiB
C#
640 lines
22 KiB
C#
using SpaceGame.Simulation.Api.Data;
|
|
|
|
namespace SpaceGame.Simulation.Api.Simulation;
|
|
|
|
public sealed partial class ScenarioLoader
|
|
{
|
|
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 (includeSolSystem && systems.All((system) => system.Id != "sol"))
|
|
{
|
|
systems.Add(CreateSolSystem());
|
|
}
|
|
|
|
return systems;
|
|
}
|
|
|
|
private static List<SolarSystemDefinition> ExpandSystems(
|
|
IReadOnlyList<SolarSystemDefinition> authoredSystems,
|
|
int targetSystemCount)
|
|
{
|
|
var systems = authoredSystems
|
|
.Select(CloneSystemDefinition)
|
|
.ToList();
|
|
|
|
if (targetSystemCount <= 0)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
if (systems.Count > targetSystemCount)
|
|
{
|
|
return TrimSystemsToTarget(systems, targetSystemCount);
|
|
}
|
|
|
|
if (systems.Count >= targetSystemCount || authoredSystems.Count == 0)
|
|
{
|
|
return systems;
|
|
}
|
|
|
|
var existingIds = systems
|
|
.Select((system) => system.Id)
|
|
.ToHashSet(StringComparer.Ordinal);
|
|
var generatedPositions = BuildGalaxyPositions(
|
|
authoredSystems.Select((system) => ToVector(system.Position)).ToList(),
|
|
targetSystemCount - systems.Count);
|
|
|
|
for (var index = systems.Count; index < targetSystemCount; index += 1)
|
|
{
|
|
var template = authoredSystems[index % authoredSystems.Count];
|
|
var name = GeneratedSystemNames[(index - authoredSystems.Count) % GeneratedSystemNames.Length];
|
|
var id = BuildGeneratedSystemId(name, index + 1);
|
|
while (!existingIds.Add(id))
|
|
{
|
|
id = $"{id}-x";
|
|
}
|
|
|
|
systems.Add(CreateGeneratedSystem(template, name, id, index - authoredSystems.Count, generatedPositions[index - authoredSystems.Count]));
|
|
}
|
|
|
|
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[] { 2.6f, 0.02f, -0.42f },
|
|
new[] { -2.4f, -0.04f, 0.56f },
|
|
new[] { 0.52f, 0.04f, 2.48f },
|
|
};
|
|
|
|
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,
|
|
string id,
|
|
int generatedIndex,
|
|
Vector3 position)
|
|
{
|
|
var starProfile = SelectStarProfile(generatedIndex);
|
|
var planets = BuildGeneratedPlanets(template, generatedIndex);
|
|
|
|
var resourceNodes = BuildProceduralResourceNodes(template, planets, generatedIndex)
|
|
.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,
|
|
})
|
|
.ToList();
|
|
|
|
return new SolarSystemDefinition
|
|
{
|
|
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),
|
|
AsteroidField = new AsteroidFieldDefinition
|
|
{
|
|
DecorationCount = template.AsteroidField.DecorationCount + ((generatedIndex % 5) * 10),
|
|
RadiusOffset = template.AsteroidField.RadiusOffset + ((generatedIndex % 4) * 18000f),
|
|
RadiusVariance = template.AsteroidField.RadiusVariance + ((generatedIndex % 3) * 12000f),
|
|
HeightVariance = template.AsteroidField.HeightVariance + ((generatedIndex % 4) * 4000f),
|
|
},
|
|
ResourceNodes = resourceNodes,
|
|
Planets = planets,
|
|
};
|
|
}
|
|
|
|
private static SolarSystemDefinition CloneSystemDefinition(SolarSystemDefinition definition)
|
|
{
|
|
return new SolarSystemDefinition
|
|
{
|
|
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,
|
|
AsteroidField = new AsteroidFieldDefinition
|
|
{
|
|
DecorationCount = definition.AsteroidField.DecorationCount,
|
|
RadiusOffset = definition.AsteroidField.RadiusOffset,
|
|
RadiusVariance = definition.AsteroidField.RadiusVariance,
|
|
HeightVariance = definition.AsteroidField.HeightVariance,
|
|
},
|
|
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,
|
|
})
|
|
.ToList(),
|
|
Planets = definition.Planets
|
|
.Select((planet) => new PlanetDefinition
|
|
{
|
|
Label = planet.Label,
|
|
PlanetType = planet.PlanetType,
|
|
Shape = planet.Shape,
|
|
MoonCount = planet.MoonCount,
|
|
OrbitRadius = planet.OrbitRadius,
|
|
OrbitSpeed = planet.OrbitSpeed,
|
|
OrbitEccentricity = planet.OrbitEccentricity,
|
|
OrbitInclination = planet.OrbitInclination,
|
|
OrbitLongitudeOfAscendingNode = planet.OrbitLongitudeOfAscendingNode,
|
|
OrbitArgumentOfPeriapsis = planet.OrbitArgumentOfPeriapsis,
|
|
OrbitPhaseAtEpoch = planet.OrbitPhaseAtEpoch,
|
|
Size = planet.Size,
|
|
Color = planet.Color,
|
|
Tilt = planet.Tilt,
|
|
HasRing = planet.HasRing,
|
|
})
|
|
.ToList(),
|
|
};
|
|
}
|
|
|
|
private static List<ResourceNodeDefinition> BuildProceduralResourceNodes(
|
|
SolarSystemDefinition template,
|
|
IReadOnlyList<PlanetDefinition> planets,
|
|
int generatedIndex)
|
|
{
|
|
var nodes = new List<ResourceNodeDefinition>();
|
|
if (template.ResourceNodes.Count > 0)
|
|
{
|
|
nodes.AddRange(template.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,
|
|
}));
|
|
}
|
|
|
|
nodes.AddRange(BuildAsteroidBeltNodes(generatedIndex, planets));
|
|
nodes.AddRange(BuildGasCloudNodes(generatedIndex, planets));
|
|
return nodes;
|
|
}
|
|
|
|
private static List<Vector3> BuildGalaxyPositions(IReadOnlyCollection<Vector3> occupiedPositions, int count)
|
|
{
|
|
var allPositions = occupiedPositions.ToList();
|
|
var generated = new List<Vector3>(count);
|
|
|
|
for (var index = 0; index < count; index += 1)
|
|
{
|
|
Vector3? accepted = null;
|
|
for (var attempt = 0; attempt < 64; attempt += 1)
|
|
{
|
|
var candidate = ComputeGeneratedSystemPosition(index, attempt);
|
|
if (allPositions.All((existing) => existing.DistanceTo(candidate) >= MinimumSystemSeparation))
|
|
{
|
|
accepted = candidate;
|
|
break;
|
|
}
|
|
}
|
|
|
|
accepted ??= ComputeFallbackGeneratedSystemPosition(index);
|
|
generated.Add(accepted.Value);
|
|
allPositions.Add(accepted.Value);
|
|
}
|
|
|
|
return generated;
|
|
}
|
|
|
|
private static Vector3 ComputeGeneratedSystemPosition(int generatedIndex, int attempt)
|
|
{
|
|
const int armCount = 4;
|
|
const float baseInnerRadius = 9f;
|
|
const float radiusStep = 0.54f;
|
|
const float armOffset = MathF.PI * 2f / armCount;
|
|
|
|
var armIndex = (generatedIndex + attempt) % armCount;
|
|
var armDepth = generatedIndex / armCount;
|
|
var radius = baseInnerRadius + (armDepth * radiusStep) + Jitter(generatedIndex * 17 + attempt, 0, 0.9f);
|
|
var angle = (armIndex * armOffset) + (radius / 8.2f) + Jitter(generatedIndex, 1 + attempt, 0.16f);
|
|
var x = MathF.Cos(angle) * radius;
|
|
var z = MathF.Sin(angle) * radius * 0.58f;
|
|
var y = ComputeSystemHeight(radius, generatedIndex, attempt);
|
|
return new Vector3(x, y, z);
|
|
}
|
|
|
|
private static Vector3 ComputeFallbackGeneratedSystemPosition(int generatedIndex)
|
|
{
|
|
const int ringCount = 5;
|
|
const float fallbackRadius = 42f;
|
|
var angle = (generatedIndex % ringCount) * (MathF.PI * 2f / ringCount) + (generatedIndex / ringCount) * 0.22f;
|
|
var radius = fallbackRadius + (generatedIndex / ringCount) * 1.8f;
|
|
return new Vector3(
|
|
MathF.Cos(angle) * radius,
|
|
ComputeSystemHeight(radius, generatedIndex, 99),
|
|
MathF.Sin(angle) * radius * 0.6f);
|
|
}
|
|
|
|
private static string BuildGeneratedSystemId(string label, int ordinal)
|
|
{
|
|
var slug = string.Concat(label
|
|
.ToLowerInvariant()
|
|
.Select((character) => char.IsLetterOrDigit(character) ? character : '-'))
|
|
.Trim('-');
|
|
|
|
return $"gen-{ordinal}-{slug}";
|
|
}
|
|
|
|
private static IEnumerable<ResourceNodeDefinition> BuildAsteroidBeltNodes(int generatedIndex, IReadOnlyList<PlanetDefinition> planets)
|
|
{
|
|
var nodeCount = 4 + (generatedIndex % 4);
|
|
var oreAmount = 1000f;
|
|
|
|
for (var index = 0; index < nodeCount; index += 1)
|
|
{
|
|
yield return new ResourceNodeDefinition
|
|
{
|
|
SourceKind = "asteroid-belt",
|
|
Angle = ((MathF.PI * 2f) / nodeCount) * index + Jitter(generatedIndex, 180 + index, 0.22f),
|
|
RadiusOffset = 120000f + Jitter(generatedIndex, 200 + index, 36000f),
|
|
InclinationDegrees = Jitter(generatedIndex, 280 + index, 12f),
|
|
AnchorPlanetIndex = ResolveAsteroidAnchorPlanetIndex(planets),
|
|
OreAmount = oreAmount,
|
|
ItemId = "ore",
|
|
ShardCount = 6 + (index % 4),
|
|
};
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<ResourceNodeDefinition> BuildGasCloudNodes(int generatedIndex, IReadOnlyList<PlanetDefinition> planets)
|
|
{
|
|
var gasAnchor = planets
|
|
.Where((planet) => planet.PlanetType is "gas-giant" or "ice-giant")
|
|
.OrderByDescending((planet) => planet.OrbitRadius)
|
|
.FirstOrDefault();
|
|
|
|
if (gasAnchor is null)
|
|
{
|
|
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 = 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 = 170000f + Jitter(generatedIndex, 260 + index, 44000f),
|
|
InclinationDegrees = Jitter(generatedIndex, 320 + index, 10f),
|
|
AnchorPlanetIndex = gasAnchorIndex,
|
|
OreAmount = gasAmount,
|
|
ItemId = "gas",
|
|
ShardCount = 10 + index,
|
|
};
|
|
}
|
|
}
|
|
|
|
private static int ResolveAsteroidAnchorPlanetIndex(IReadOnlyList<PlanetDefinition> planets)
|
|
{
|
|
if (planets.Count == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
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(
|
|
SolarSystemDefinition template,
|
|
int generatedIndex)
|
|
{
|
|
var planetCount = 2 + (int)MathF.Floor(Hash01(generatedIndex, 2) * 7f);
|
|
var planets = new List<PlanetDefinition>(planetCount);
|
|
var orbitRadius = 0.24f + (Hash01(generatedIndex, 3) * 0.12f);
|
|
var sourcePlanets = template.Planets.Count > 0 ? template.Planets : null;
|
|
|
|
for (var index = 0; index < planetCount; index += 1)
|
|
{
|
|
var profile = SelectPlanetProfile(generatedIndex, index);
|
|
var templatePlanet = sourcePlanets is not null && sourcePlanets.Count > 0
|
|
? sourcePlanets[index % sourcePlanets.Count]
|
|
: null;
|
|
|
|
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);
|
|
|
|
planets.Add(new PlanetDefinition
|
|
{
|
|
Label = $"{BuildPlanetBaseName(generatedIndex, index)}-{index + 1}",
|
|
PlanetType = profile.Type,
|
|
Shape = profile.Shape,
|
|
MoonCount = profile.BaseMoonCount + moonVariance,
|
|
OrbitRadius = orbitRadius,
|
|
OrbitSpeed = 0.11f / MathF.Sqrt(MathF.Max(orbitRadius * orbitRadius * orbitRadius, 0.02f)),
|
|
OrbitEccentricity = orbitEccentricity,
|
|
OrbitInclination = orbitInclination,
|
|
OrbitLongitudeOfAscendingNode = Hash01(generatedIndex, 120 + index) * 360f,
|
|
OrbitArgumentOfPeriapsis = Hash01(generatedIndex, 140 + index) * 360f,
|
|
OrbitPhaseAtEpoch = Hash01(generatedIndex, 160 + index) * 360f,
|
|
Size = profile.BaseSize + (Hash01(generatedIndex, 50 + index) * (profile.BaseSize * 0.35f)),
|
|
Color = templatePlanet?.Color ?? profile.Color,
|
|
Tilt = -0.45f + (Hash01(generatedIndex, 60 + index) * 0.9f),
|
|
HasRing = profile.CanHaveRing && Hash01(generatedIndex, 70 + index) > 0.55f,
|
|
});
|
|
}
|
|
|
|
return planets;
|
|
}
|
|
|
|
private static StarProfile SelectStarProfile(int generatedIndex)
|
|
{
|
|
var value = Hash01(generatedIndex, 80);
|
|
return value switch
|
|
{
|
|
< 0.32f => StarProfiles[0],
|
|
< 0.54f => StarProfiles[1],
|
|
< 0.68f => StarProfiles[5],
|
|
< 0.8f => StarProfiles[2],
|
|
< 0.9f => StarProfiles[3],
|
|
< 0.97f => StarProfiles[6],
|
|
_ => StarProfiles[4],
|
|
};
|
|
}
|
|
|
|
private static PlanetProfile SelectPlanetProfile(int generatedIndex, int planetIndex)
|
|
{
|
|
var value = Hash01(generatedIndex, 90 + planetIndex);
|
|
return value switch
|
|
{
|
|
< 0.14f => PlanetProfiles[7],
|
|
< 0.28f => PlanetProfiles[0],
|
|
< 0.46f => PlanetProfiles[3],
|
|
< 0.62f => PlanetProfiles[1],
|
|
< 0.74f => PlanetProfiles[2],
|
|
< 0.86f => PlanetProfiles[4],
|
|
< 0.94f => PlanetProfiles[6],
|
|
_ => PlanetProfiles[5],
|
|
};
|
|
}
|
|
|
|
private static string BuildPlanetBaseName(int generatedIndex, int planetIndex)
|
|
{
|
|
var source = GeneratedSystemNames[generatedIndex % GeneratedSystemNames.Length]
|
|
.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)[0];
|
|
return source[..Math.Min(source.Length, 6)];
|
|
}
|
|
|
|
private static float ComputeSystemHeight(float radius, int generatedIndex, int salt)
|
|
{
|
|
var normalized = MathF.Min(1f, MathF.Max(0f, (radius - 8f) / 28f));
|
|
var band = 0.22f + (normalized * 0.76f);
|
|
return (Hash01(generatedIndex, 100 + salt) * 2f - 1f) * band;
|
|
}
|
|
|
|
private static float Jitter(int index, int salt, float amplitude) =>
|
|
(Hash01(index, salt) * 2f - 1f) * amplitude;
|
|
|
|
private static float Hash01(int index, int salt)
|
|
{
|
|
uint value = (uint)(index + 1);
|
|
value ^= (uint)(salt + 0x9e3779b9);
|
|
value *= 0x85ebca6b;
|
|
value ^= value >> 13;
|
|
value *= 0xc2b2ae35;
|
|
value ^= value >> 16;
|
|
return (value & 0x00ffffff) / 16777215f;
|
|
}
|
|
|
|
private sealed record StarProfile(
|
|
string Kind,
|
|
string StarColor,
|
|
string StarGlow,
|
|
float BaseSize,
|
|
int StarCount);
|
|
|
|
private sealed record PlanetProfile(
|
|
string Type,
|
|
string Shape,
|
|
string Color,
|
|
float BaseSize,
|
|
float OrbitGapMin,
|
|
int BaseMoonCount,
|
|
bool CanHaveRing)
|
|
{
|
|
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 },
|
|
new ResourceNodeDefinition { SourceKind = "gas-cloud", Angle = 0.9f, RadiusOffset = 210000f, InclinationDegrees = 3f, AnchorPlanetIndex = 4, OreAmount = 1000f, ItemId = "gas", ShardCount = 12 },
|
|
new ResourceNodeDefinition { SourceKind = "gas-cloud", Angle = 2.7f, RadiusOffset = 228000f, InclinationDegrees = -4f, AnchorPlanetIndex = 5, OreAmount = 1000f, ItemId = "gas", ShardCount = 12 },
|
|
new ResourceNodeDefinition { SourceKind = "gas-cloud", Angle = 4.8f, RadiusOffset = 186000f, InclinationDegrees = 6f, AnchorPlanetIndex = 6, OreAmount = 1000f, ItemId = "gas", ShardCount = 10 },
|
|
],
|
|
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);
|
|
}
|
|
}
|