Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2020-12-22 03:13:43 +00:00
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
|
|
using System;
|
2020-03-22 19:05:26 +00:00
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Linq;
|
2026-08-10 14:57:50 +01:00
|
|
|
using MessagePack;
|
2020-12-01 00:16:11 +00:00
|
|
|
using UniRx;
|
2026-09-14 17:39:12 +02:00
|
|
|
using CultMath;
|
|
|
|
|
using static CultMath.math;
|
|
|
|
|
using float2 = CultMath.float2;
|
|
|
|
|
using Random = CultMath.Random;
|
2020-03-22 19:05:26 +00:00
|
|
|
|
2021-03-18 00:36:34 +00:00
|
|
|
public static class ZoneGenerator
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2021-03-08 22:58:37 +00:00
|
|
|
private class Circle
|
|
|
|
|
{
|
|
|
|
|
public float2 Center;
|
|
|
|
|
public float Radius;
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
public float Area => PI * Radius * Radius;
|
2021-03-08 22:58:37 +00:00
|
|
|
|
|
|
|
|
public Circle(float2 center, float radius)
|
|
|
|
|
{
|
|
|
|
|
Center = center;
|
|
|
|
|
Radius = radius;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
public float DistanceTo(float2 point) => length(point - Center) - Radius;
|
|
|
|
|
public float DistanceTo(Circle other) => length(other.Center - Center) - Radius - other.Radius;
|
2021-03-08 22:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private const int MaximumPlacementSamples = 32;
|
2021-03-15 11:40:46 +00:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
public static ZonePack GenerateZone(
|
2021-03-18 00:36:34 +00:00
|
|
|
ItemManager itemManager,
|
|
|
|
|
ZoneGenerationSettings zoneSettings,
|
2021-05-14 03:03:28 +01:00
|
|
|
Galaxy galaxy,
|
|
|
|
|
GalaxyZone galaxyZone,
|
2021-04-22 02:53:06 +01:00
|
|
|
bool isTutorial = false)
|
2021-03-18 00:36:34 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var pack = new ZonePack();
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
var cache = itemManager.ItemData;
|
2021-03-18 00:36:34 +00:00
|
|
|
|
2026-09-14 22:09:37 +02:00
|
|
|
var random = new Random(galaxyZone.Name.StableHash() ^ (uint) pcg3d(galaxyZone.Position).x);
|
2021-03-18 00:36:34 +00:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
var density = saturate(galaxy.Background.CloudDensity(galaxyZone.Position)/2);
|
|
|
|
|
pack.Radius = zoneSettings.ZoneRadius.Evaluate(density);
|
|
|
|
|
pack.Mass = zoneSettings.ZoneMass.Evaluate(density);
|
2021-03-18 00:36:34 +00:00
|
|
|
var targetSubzoneCount = zoneSettings.SubZoneCount.Evaluate(density);
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-30 17:54:54 +01:00
|
|
|
//Debug.Log($"Generating zone at position {zone.Position} with radius {zoneRadius} and mass {zoneMass}");
|
2021-03-08 22:58:37 +00:00
|
|
|
|
|
|
|
|
var planets = new List<GeneratorPlanet>();
|
|
|
|
|
if (targetSubzoneCount > 1)
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var zoneBoundary = new Circle(float2.zero, pack.Radius * zoneSettings.ZoneBoundaryRadius);
|
2021-03-08 22:58:37 +00:00
|
|
|
float boundaryTangentRadius(float2 point) => -zoneBoundary.DistanceTo(point);
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2021-03-08 22:58:37 +00:00
|
|
|
var occupiedAreas = new List<Circle>();
|
2026-08-10 14:57:50 +01:00
|
|
|
float tangentRadius(float2 point) => min(boundaryTangentRadius(point), occupiedAreas.Min(circle => circle.DistanceTo(point)));
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
var startPosition = random.NextFloat(pack.Radius * .25f, pack.Radius * .5f) * random.NextFloat2Direction();
|
2021-03-08 22:58:37 +00:00
|
|
|
occupiedAreas.Add(new Circle(startPosition, boundaryTangentRadius(startPosition)));
|
|
|
|
|
|
|
|
|
|
int samples = 0;
|
|
|
|
|
while (occupiedAreas.Count < targetSubzoneCount && samples < MaximumPlacementSamples)
|
|
|
|
|
{
|
|
|
|
|
samples = 0;
|
|
|
|
|
for (int i = 0; i < MaximumPlacementSamples; i++)
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var samplePos = random.NextFloat2(-pack.Radius, pack.Radius);
|
2021-03-08 22:58:37 +00:00
|
|
|
var rad = tangentRadius(samplePos);
|
|
|
|
|
if (rad > 0)
|
|
|
|
|
{
|
|
|
|
|
occupiedAreas.Add(new Circle(samplePos, rad));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
samples++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var totalArea = occupiedAreas.Sum(c => c.Area);
|
|
|
|
|
foreach (var c in occupiedAreas)
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
planets.AddRange(GenerateEntities(zoneSettings, ref random, c.Area / totalArea * pack.Mass, c.Radius, c.Center));
|
2021-03-08 22:58:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2026-08-10 14:57:50 +01:00
|
|
|
planets.AddRange(GenerateEntities(zoneSettings, ref random, pack.Mass, pack.Radius, float2.zero));
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
|
|
|
|
// Every orbit is a record; parents link to records, so each orbit holds its key before any child names it
|
2026-08-10 14:57:50 +01:00
|
|
|
var orbitMap = new Dictionary<GeneratorPlanet, OrbitData>();
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
foreach (var planet in planets)
|
|
|
|
|
cache.Upsert(orbitMap[planet] = new OrbitData
|
2020-03-30 03:11:57 +01:00
|
|
|
{
|
2021-03-08 22:58:37 +00:00
|
|
|
FixedPosition = planet.FixedPosition,
|
2025-07-18 00:21:33 +01:00
|
|
|
Distance = planet.Distance,
|
2020-11-27 22:47:33 +00:00
|
|
|
Phase = planet.Phase
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
});
|
|
|
|
|
foreach (var planet in planets.Where(p => p.Parent != null))
|
|
|
|
|
{
|
|
|
|
|
orbitMap[planet].Parent = cache.RefOf(orbitMap[planet.Parent]);
|
|
|
|
|
cache.Upsert(orbitMap[planet]);
|
|
|
|
|
}
|
|
|
|
|
pack.Orbits = planets.Select(planet => cache.RefOf(orbitMap[planet])).ToList();
|
2020-03-30 03:11:57 +01:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
pack.Planets = planets.Where(p=>!p.Empty).Select(planet =>
|
2020-03-30 03:11:57 +01:00
|
|
|
{
|
2020-11-27 22:47:33 +00:00
|
|
|
BodyType bodyType = planet.Belt ? BodyType.Asteroid :
|
2021-03-18 00:36:34 +00:00
|
|
|
planet.Mass > zoneSettings.SunMass ? BodyType.Sun :
|
|
|
|
|
planet.Mass > zoneSettings.GasGiantMass ? BodyType.GasGiant :
|
|
|
|
|
planet.Mass > zoneSettings.PlanetMass ? BodyType.Planet : BodyType.Planetoid;
|
2020-05-02 01:30:33 +01:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
BodyData planetData;
|
2020-11-27 22:47:33 +00:00
|
|
|
switch (bodyType)
|
|
|
|
|
{
|
|
|
|
|
case BodyType.Asteroid:
|
2026-08-10 14:57:50 +01:00
|
|
|
planetData = new AsteroidBeltData();
|
2020-11-27 22:47:33 +00:00
|
|
|
break;
|
|
|
|
|
case BodyType.Planetoid:
|
|
|
|
|
case BodyType.Planet:
|
2026-08-10 14:57:50 +01:00
|
|
|
planetData = new PlanetData();
|
2020-11-27 22:47:33 +00:00
|
|
|
break;
|
|
|
|
|
case BodyType.GasGiant:
|
2026-08-10 14:57:50 +01:00
|
|
|
planetData = new GasGiantData();
|
2020-11-27 22:47:33 +00:00
|
|
|
break;
|
|
|
|
|
case BodyType.Sun:
|
2026-08-10 14:57:50 +01:00
|
|
|
planetData = new SunData();
|
2020-11-27 22:47:33 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 00:21:33 +01:00
|
|
|
planetData.Mass = planet.Mass;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
planetData.Orbit = cache.RefOf(orbitMap[planet]);
|
2026-08-10 14:57:50 +01:00
|
|
|
if (planetData is AsteroidBeltData beltData)
|
2020-05-02 01:30:33 +01:00
|
|
|
{
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
beltData.Asteroids =
|
2025-07-18 00:21:33 +01:00
|
|
|
Enumerable.Range(0, (int) zoneSettings.AsteroidCount.Evaluate(beltData.Mass * orbitMap[planet].Distance))
|
2020-05-28 04:50:40 +01:00
|
|
|
.Select(_ => new Asteroid
|
|
|
|
|
{
|
2025-07-18 00:21:33 +01:00
|
|
|
Distance = orbitMap[planet].Distance + random.NextFloat()*(random.NextFloat()-.5f)*zoneSettings.AsteroidBeltWidth.Evaluate(orbitMap[planet].Distance),
|
2020-12-01 00:16:11 +00:00
|
|
|
Phase = random.NextFloat(),
|
|
|
|
|
Size = random.NextFloat(),
|
2021-03-18 00:36:34 +00:00
|
|
|
RotationSpeed = zoneSettings.AsteroidRotationSpeed.Evaluate(random.NextFloat())
|
2020-12-01 00:16:11 +00:00
|
|
|
})
|
|
|
|
|
//.OrderByDescending(a=>a.Size)
|
|
|
|
|
.ToArray();
|
2020-05-02 01:30:33 +01:00
|
|
|
}
|
2026-08-10 14:57:50 +01:00
|
|
|
else if (planetData is GasGiantData gas)
|
2020-11-27 22:47:33 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
if (gas is SunData sun)
|
|
|
|
|
{
|
2020-12-01 20:38:28 +00:00
|
|
|
float primary = random.NextFloat();
|
2026-08-10 14:57:50 +01:00
|
|
|
float secondary = frac(primary + 1 + zoneSettings.SunSecondaryColorDistance * (random.NextFloat() > .5 ? 1 : -1));
|
2025-07-18 00:21:33 +01:00
|
|
|
gas.Colors = new []
|
2020-12-01 20:38:28 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
float4(ColorMath.HsvToRgb(float3(primary, zoneSettings.SunColorSaturation, .5f)), 0),
|
|
|
|
|
float4(ColorMath.HsvToRgb(float3(secondary, zoneSettings.SunColorSaturation, 1)), 1)
|
2020-12-01 20:38:28 +00:00
|
|
|
};
|
2026-08-10 14:57:50 +01:00
|
|
|
sun.FogTintColor = ColorMath.HsvToRgb(float3(primary, zoneSettings.SunFogTintSaturation, 1));
|
|
|
|
|
sun.LightColor = ColorMath.HsvToRgb(float3(primary, zoneSettings.SunLightSaturation, 1));
|
2025-07-18 00:21:33 +01:00
|
|
|
gas.FirstOffsetDomainRotationSpeed = 5;
|
2020-12-01 20:38:28 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Define primary color and two adjacent colors
|
|
|
|
|
float primary = random.NextFloat();
|
2026-08-10 14:57:50 +01:00
|
|
|
float right = frac(primary + zoneSettings.GasGiantBandColorSeparation);
|
|
|
|
|
float left = frac(primary + 1 - zoneSettings.GasGiantBandColorSeparation);
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-12-01 20:38:28 +00:00
|
|
|
// Create n time keys from 0 to 1
|
2021-03-18 00:36:34 +00:00
|
|
|
var bandCount = (int) (zoneSettings.GasGiantBandCount.Evaluate(random.NextFloat()) + .5f);
|
2020-12-01 20:38:28 +00:00
|
|
|
var times = Enumerable.Range(0, bandCount)
|
|
|
|
|
.Select(i => (float) i / (bandCount-1));
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-12-01 20:38:28 +00:00
|
|
|
// Each band has a chance of being either the primary or one of the adjacent hues
|
|
|
|
|
// Saturation and Value are random with curves applied
|
2025-07-18 00:21:33 +01:00
|
|
|
gas.Colors = times
|
2026-08-10 14:57:50 +01:00
|
|
|
.Select(time => float4(ColorMath.HsvToRgb(float3(
|
2021-03-18 00:36:34 +00:00
|
|
|
random.NextFloat() > zoneSettings.GasGiantBandAltColorChance ? primary : (random.NextFloat() > .5f ? right : left),
|
|
|
|
|
zoneSettings.GasGiantBandSaturation.Evaluate(random.NextFloat()),
|
2026-08-10 14:57:50 +01:00
|
|
|
zoneSettings.GasGiantBandSaturation.Evaluate(random.NextFloat()))),time))
|
2020-12-01 20:38:28 +00:00
|
|
|
.ToArray();
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2025-07-18 00:21:33 +01:00
|
|
|
gas.FirstOffsetDomainRotationSpeed = 0;
|
2020-12-01 20:38:28 +00:00
|
|
|
}
|
2025-07-18 00:21:33 +01:00
|
|
|
gas.AlbedoRotationSpeed = -3;
|
|
|
|
|
gas.FirstOffsetRotationSpeed = 5;
|
|
|
|
|
gas.SecondOffsetRotationSpeed = 10;
|
|
|
|
|
gas.SecondOffsetDomainRotationSpeed = -25;
|
2020-11-27 22:47:33 +00:00
|
|
|
}
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
|
|
|
|
// A body is named for the first eight characters of the key the cache mints for it
|
|
|
|
|
planetData.Name = cache.Upsert(planetData).Key.Value.Substring(0, 8);
|
|
|
|
|
return cache.Upsert(planetData);
|
2020-11-27 22:47:33 +00:00
|
|
|
}).ToList();
|
2020-03-30 03:11:57 +01:00
|
|
|
|
2021-05-14 03:03:28 +01:00
|
|
|
var nearestFaction = galaxy.Factions.MinBy(f => galaxy.HomeZones[f].Distance[galaxyZone]);
|
|
|
|
|
var nearestFactionHomeZone = galaxy.HomeZones[nearestFaction];
|
|
|
|
|
var factionPresence = nearestFaction.InfluenceDistance - nearestFactionHomeZone.Distance[galaxyZone] + 1;
|
2021-05-02 20:59:45 +01:00
|
|
|
|
2021-05-16 05:24:51 +01:00
|
|
|
var storyStations = galaxyZone.Locations.Where(story => story.Type == LocationType.Station).ToArray();
|
|
|
|
|
var stationCount = (int)(random.NextFloat() * (factionPresence + 1)) + storyStations.Length;
|
Tutorial station: ZoneGenerator.GenerateZone always seats the entrance zone (operator ruling)
Operator, 2026-09-25 (docs/locomotion-cut.md): the tutorial's entrance zone always gets a
generated faction station with a docking bay, deterministically. Structurally, three defects made
that false on every seed: Galaxy.cs halves influence and picks the lowest-influence unowned zone
for Entrance, so stationCount rolled 0; a Lagrange orbit is only available when a planet has an
orbiting sibling, which a boring or empty system never has; and a null station loadout silently
skipped the slot.
GenerateZone now identifies the entrance zone by reference against the galaxy parameter it already
takes (isTutorial && galaxyZone == galaxy.Entrance), never by name:
- stationCount is floored to 1 for the entrance zone, after the roll that produced it.
- When no Lagrange orbit exists, a fallback orbit fixed at half the zone radius from centre is used
instead of leaving selectedStationOrbits empty. CreateLagrangeOrbit now carries FixedPosition
through so a Parent-less base orbit (the fallback) still positions correctly.
- A null station loadout throws for the entrance zone instead of continuing past it.
- The enemy count is based on the pre-override station count, so forcing the entrance's station
doesn't also hand it a bonus enemy.
The story-station path (Locations/LocationStory) is not used: it isn't saved, and
EntitySerializer.cs:77 would break Continue, per the map's own ruling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 17:40:31 +02:00
|
|
|
// Enemy count below is based on this pre-override value, so the tutorial entrance doesn't gain an enemy
|
|
|
|
|
// just because its station got forced.
|
|
|
|
|
var baseStationCount = stationCount;
|
|
|
|
|
// Operator ruling, 2026-09-25 (docs/locomotion-cut.md, "Tutorial station"): the entrance zone always
|
|
|
|
|
// gets a generated faction station with a docking bay, deterministically. Galaxy.cs halves influence and
|
|
|
|
|
// picks the lowest-influence unowned zone for Entrance, which otherwise rolls stationCount 0 on every
|
|
|
|
|
// seed. galaxy.Entrance is reference-identity, not a name comparison.
|
|
|
|
|
var isTutorialEntrance = isTutorial && galaxyZone == galaxy.Entrance;
|
|
|
|
|
if (isTutorialEntrance && stationCount < 1) stationCount = 1;
|
2021-05-02 20:59:45 +01:00
|
|
|
var potentialLagrangePoints = planets
|
|
|
|
|
.Where(p => p.Parent != null && p.Parent.Children
|
2026-08-10 14:57:50 +01:00
|
|
|
.TrueForAll(c => !(c != p && abs(c.Distance - p.Distance) < .1f))) // Filter Rosettes
|
2021-05-02 20:59:45 +01:00
|
|
|
.OrderBy(p => p.Distance)
|
|
|
|
|
.ToArray();
|
2026-09-25 20:15:00 +02:00
|
|
|
// The entrance's own station count is the floor above; its orbit always comes from this same selection.
|
|
|
|
|
// Ordinarily that selection is potentialLagrangePoints (non-rosette planet orbits). If the entrance zone
|
|
|
|
|
// has none of those (a boring single-body system, or a system that is entirely one rosette), widen to
|
|
|
|
|
// every planet orbit ordinary placement's own candidate pool is drawn from before the rosette filter --
|
|
|
|
|
// still a real, parented planet orbit with a positive Distance -- rather than inventing an unparented,
|
|
|
|
|
// zero-distance orbit (that gave PlaceTurret's phase = 20*multiplier/orbit.Distance a division by zero,
|
|
|
|
|
// and left an orbit record with no planet behind it).
|
|
|
|
|
var lagrangeCandidates = potentialLagrangePoints;
|
|
|
|
|
if (isTutorialEntrance && lagrangeCandidates.Length == 0)
|
|
|
|
|
lagrangeCandidates = planets.Where(p => p.Parent != null).OrderBy(p => p.Distance).ToArray();
|
2021-05-02 20:59:45 +01:00
|
|
|
// Pick a selection from the middle of the distribution
|
2026-09-25 20:15:00 +02:00
|
|
|
var selectedStationOrbits = lagrangeCandidates
|
|
|
|
|
.Skip(lagrangeCandidates.Length / 2)
|
2021-05-02 20:59:45 +01:00
|
|
|
.Take(stationCount)
|
|
|
|
|
.Select(p=>orbitMap[p])
|
|
|
|
|
.ToArray();
|
2026-09-26 01:23:43 +02:00
|
|
|
if (isTutorialEntrance && lagrangeCandidates.Length == 0)
|
2026-09-25 20:15:00 +02:00
|
|
|
throw new InvalidOperationException(
|
2026-09-26 01:23:43 +02:00
|
|
|
"The tutorial entrance zone must always get a station, but it has no candidate orbit at all -- every planet in the zone is a parentless root.");
|
2021-05-16 05:24:51 +01:00
|
|
|
|
|
|
|
|
var loadoutGenerators = new Dictionary<Faction, LoadoutGenerator>();
|
|
|
|
|
|
|
|
|
|
LoadoutGenerator GetLoadoutGenerator(Faction faction)
|
|
|
|
|
{
|
|
|
|
|
if (!loadoutGenerators.ContainsKey(faction))
|
2026-08-10 14:57:50 +01:00
|
|
|
loadoutGenerators[faction] = new LoadoutGenerator(ref random, itemManager, galaxy, galaxyZone, faction, .5f);
|
2021-05-16 05:24:51 +01:00
|
|
|
return loadoutGenerators[faction];
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
OrbitData CreateLagrangeOrbit(OrbitData baseOrbit)
|
2021-03-18 00:36:34 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var lagrangeOrbit = new OrbitData
|
2021-05-02 20:59:45 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
Parent = baseOrbit.Parent,
|
2025-07-18 00:21:33 +01:00
|
|
|
Distance = baseOrbit.Distance,
|
Tutorial station: ZoneGenerator.GenerateZone always seats the entrance zone (operator ruling)
Operator, 2026-09-25 (docs/locomotion-cut.md): the tutorial's entrance zone always gets a
generated faction station with a docking bay, deterministically. Structurally, three defects made
that false on every seed: Galaxy.cs halves influence and picks the lowest-influence unowned zone
for Entrance, so stationCount rolled 0; a Lagrange orbit is only available when a planet has an
orbiting sibling, which a boring or empty system never has; and a null station loadout silently
skipped the slot.
GenerateZone now identifies the entrance zone by reference against the galaxy parameter it already
takes (isTutorial && galaxyZone == galaxy.Entrance), never by name:
- stationCount is floored to 1 for the entrance zone, after the roll that produced it.
- When no Lagrange orbit exists, a fallback orbit fixed at half the zone radius from centre is used
instead of leaving selectedStationOrbits empty. CreateLagrangeOrbit now carries FixedPosition
through so a Parent-less base orbit (the fallback) still positions correctly.
- A null station loadout throws for the entrance zone instead of continuing past it.
- The enemy count is based on the pre-override station count, so forcing the entrance's station
doesn't also hand it a bonus enemy.
The story-station path (Locations/LocationStory) is not used: it isn't saved, and
EntitySerializer.cs:77 would break Continue, per the map's own ruling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 17:40:31 +02:00
|
|
|
Phase = baseOrbit.Phase + PI / 3 * sign(random.NextFloat() - .5f),
|
2021-05-02 20:59:45 +01:00
|
|
|
};
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
pack.Orbits.Add(cache.Upsert(lagrangeOrbit));
|
2021-05-16 05:24:51 +01:00
|
|
|
return lagrangeOrbit;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
void PlaceTurret(OrbitData orbit, LoadoutGenerator loadoutGenerator, int distanceMultiplier)
|
2021-05-16 05:24:51 +01:00
|
|
|
{
|
2025-07-18 00:21:33 +01:00
|
|
|
var phase = 20f * distanceMultiplier / orbit.Distance;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
var turretOrbit = new OrbitData
|
2021-05-02 20:59:45 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
Parent = orbit.Parent,
|
2025-07-18 00:21:33 +01:00
|
|
|
Distance = orbit.Distance,
|
2021-05-16 05:24:51 +01:00
|
|
|
Phase = orbit.Phase + phase
|
2021-05-02 20:59:45 +01:00
|
|
|
};
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
pack.Orbits.Add(cache.Upsert(turretOrbit));
|
2021-05-16 05:24:51 +01:00
|
|
|
var turret = loadoutGenerator.GenerateTurretLoadout();
|
2022-07-12 14:22:23 +01:00
|
|
|
if (turret == null) return;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
turret.Orbit = cache.RefOf(turretOrbit);
|
2026-08-10 14:57:50 +01:00
|
|
|
pack.Entities.Add(turret);
|
2021-05-16 05:24:51 +01:00
|
|
|
}
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
void PlaceTurrets(OrbitData orbit, LoadoutGenerator loadoutGenerator, int count)
|
2021-05-16 05:24:51 +01:00
|
|
|
{
|
|
|
|
|
for(int t=0; t<count; t++)
|
2021-05-02 20:59:45 +01:00
|
|
|
{
|
2021-05-16 05:24:51 +01:00
|
|
|
var dist = t / 2;
|
|
|
|
|
if (t % 2 == 0) dist = -dist;
|
|
|
|
|
PlaceTurret(orbit, loadoutGenerator, dist);
|
|
|
|
|
}
|
2021-03-18 00:36:34 +00:00
|
|
|
}
|
2021-05-16 05:24:51 +01:00
|
|
|
|
|
|
|
|
for (var i = 0; i < storyStations.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var story = storyStations[i];
|
|
|
|
|
var orbit = selectedStationOrbits[i];
|
|
|
|
|
var lagrangeOrbit = CreateLagrangeOrbit(orbit);
|
|
|
|
|
var station = GetLoadoutGenerator(story.Faction).GenerateStationLoadout();
|
2026-09-12 23:22:36 +02:00
|
|
|
if (station == null)
|
|
|
|
|
{
|
|
|
|
|
itemManager.Log($"Story station for {story.Faction?.Name} could not be generated; its story has nowhere to live!");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
station.Orbit = cache.RefOf(lagrangeOrbit);
|
2021-05-16 05:24:51 +01:00
|
|
|
station.SecurityLevel = story.Security;
|
2026-08-10 14:57:50 +01:00
|
|
|
station.SecurityRadius = pack.Radius;
|
2021-05-16 05:24:51 +01:00
|
|
|
station.Story = i;
|
2026-08-10 14:57:50 +01:00
|
|
|
pack.Entities.Add(station);
|
2021-05-16 05:24:51 +01:00
|
|
|
|
|
|
|
|
PlaceTurrets(lagrangeOrbit, GetLoadoutGenerator(story.Faction), story.Turrets);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (var i = storyStations.Length; i < selectedStationOrbits.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var orbit = selectedStationOrbits[i];
|
2026-08-10 14:57:50 +01:00
|
|
|
var security = (SecurityLevel) ((int) ((1f - pow(random.NextFloat(), factionPresence / 2f)) * 3f));
|
2021-05-16 05:24:51 +01:00
|
|
|
|
|
|
|
|
var lagrangeOrbit = CreateLagrangeOrbit(orbit);
|
|
|
|
|
var station = GetLoadoutGenerator(nearestFaction).GenerateStationLoadout();
|
Tutorial station: ZoneGenerator.GenerateZone always seats the entrance zone (operator ruling)
Operator, 2026-09-25 (docs/locomotion-cut.md): the tutorial's entrance zone always gets a
generated faction station with a docking bay, deterministically. Structurally, three defects made
that false on every seed: Galaxy.cs halves influence and picks the lowest-influence unowned zone
for Entrance, so stationCount rolled 0; a Lagrange orbit is only available when a planet has an
orbiting sibling, which a boring or empty system never has; and a null station loadout silently
skipped the slot.
GenerateZone now identifies the entrance zone by reference against the galaxy parameter it already
takes (isTutorial && galaxyZone == galaxy.Entrance), never by name:
- stationCount is floored to 1 for the entrance zone, after the roll that produced it.
- When no Lagrange orbit exists, a fallback orbit fixed at half the zone radius from centre is used
instead of leaving selectedStationOrbits empty. CreateLagrangeOrbit now carries FixedPosition
through so a Parent-less base orbit (the fallback) still positions correctly.
- A null station loadout throws for the entrance zone instead of continuing past it.
- The enemy count is based on the pre-override station count, so forcing the entrance's station
doesn't also hand it a bonus enemy.
The story-station path (Locations/LocationStory) is not used: it isn't saved, and
EntitySerializer.cs:77 would break Continue, per the map's own ruling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 17:40:31 +02:00
|
|
|
if (station == null)
|
|
|
|
|
{
|
|
|
|
|
if (isTutorialEntrance)
|
|
|
|
|
throw new InvalidOperationException(
|
|
|
|
|
$"The tutorial entrance zone must always get a station, but {nearestFaction?.Name} has no station loadout to generate.");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
station.Orbit = cache.RefOf(lagrangeOrbit);
|
2021-05-16 05:24:51 +01:00
|
|
|
station.SecurityLevel = security;
|
2026-08-10 14:57:50 +01:00
|
|
|
station.SecurityRadius = pack.Radius;
|
|
|
|
|
pack.Entities.Add(station);
|
2021-05-16 05:24:51 +01:00
|
|
|
|
|
|
|
|
PlaceTurrets(lagrangeOrbit, GetLoadoutGenerator(nearestFaction), 2);
|
|
|
|
|
}
|
|
|
|
|
|
Tutorial station: ZoneGenerator.GenerateZone always seats the entrance zone (operator ruling)
Operator, 2026-09-25 (docs/locomotion-cut.md): the tutorial's entrance zone always gets a
generated faction station with a docking bay, deterministically. Structurally, three defects made
that false on every seed: Galaxy.cs halves influence and picks the lowest-influence unowned zone
for Entrance, so stationCount rolled 0; a Lagrange orbit is only available when a planet has an
orbiting sibling, which a boring or empty system never has; and a null station loadout silently
skipped the slot.
GenerateZone now identifies the entrance zone by reference against the galaxy parameter it already
takes (isTutorial && galaxyZone == galaxy.Entrance), never by name:
- stationCount is floored to 1 for the entrance zone, after the roll that produced it.
- When no Lagrange orbit exists, a fallback orbit fixed at half the zone radius from centre is used
instead of leaving selectedStationOrbits empty. CreateLagrangeOrbit now carries FixedPosition
through so a Parent-less base orbit (the fallback) still positions correctly.
- A null station loadout throws for the entrance zone instead of continuing past it.
- The enemy count is based on the pre-override station count, so forcing the entrance's station
doesn't also hand it a bonus enemy.
The story-station path (Locations/LocationStory) is not used: it isn't saved, and
EntitySerializer.cs:77 would break Continue, per the map's own ruling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 17:40:31 +02:00
|
|
|
var enemyCount = (int)(random.NextFloat() * factionPresence * 2) + baseStationCount;
|
2021-05-02 20:59:45 +01:00
|
|
|
for (int i = 0; i < enemyCount; i++)
|
|
|
|
|
{
|
2022-07-12 14:22:23 +01:00
|
|
|
var ship = GetLoadoutGenerator(nearestFaction).GenerateShipLoadout();
|
|
|
|
|
if (ship == null) continue;
|
2026-08-10 14:57:50 +01:00
|
|
|
pack.Entities.Add(ship);
|
2021-05-02 20:59:45 +01:00
|
|
|
}
|
2021-03-18 00:36:34 +00:00
|
|
|
|
2026-09-17 14:00:22 +02:00
|
|
|
// Neutral wanderers: ships belonging to a faction that neither owns the zone nor is its
|
|
|
|
|
// nearest faction, so existing hostility rules leave them non-hostile to the player.
|
|
|
|
|
// Testing affordance for hand-testing combat/targeting; none spawn if no faction qualifies.
|
|
|
|
|
var eligibleWandererFactions = EligibleWandererFactions(galaxy.Factions, galaxyZone.Owner, nearestFaction);
|
|
|
|
|
if (zoneSettings.NeutralWandererCount > 0 && eligibleWandererFactions.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
var wandererFaction = eligibleWandererFactions[random.NextInt(eligibleWandererFactions.Length)];
|
|
|
|
|
var wandererLoadoutGenerator = GetLoadoutGenerator(wandererFaction);
|
|
|
|
|
for (int i = 0; i < zoneSettings.NeutralWandererCount; i++)
|
|
|
|
|
{
|
|
|
|
|
var ship = wandererLoadoutGenerator.GenerateShipLoadout();
|
|
|
|
|
if (ship == null) continue;
|
|
|
|
|
pack.Entities.Add(ship);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
return pack;
|
2020-03-30 03:11:57 +01:00
|
|
|
}
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2026-09-17 14:00:22 +02:00
|
|
|
// The neutral-wanderer faction pool: every galaxy faction except the zone owner and the nearest
|
|
|
|
|
// faction (whose ships are already the zone's hostile-when-trespassing/enemy spawns). Extracted so
|
|
|
|
|
// the selection rule is testable without running the full zone-generation pipeline.
|
|
|
|
|
public static Faction[] EligibleWandererFactions(Faction[] factions, Faction owner, Faction nearest) =>
|
|
|
|
|
factions.Where(f => f != owner && f != nearest).ToArray();
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
// static float ResourceValue(ref Random random, ZoneGenerationSettings settings, SimpleCommodityData resource, float density)
|
|
|
|
|
// {
|
|
|
|
|
// return random.NextPowerDistribution(resource.Minimum, resource.Maximum, resource.Exponent,
|
|
|
|
|
// 1 / lerp(settings.ResourceDensityMinimum, settings.ResourceDensityMaximum, density));
|
|
|
|
|
// }
|
2026-06-13 04:56:50 +01:00
|
|
|
|
2021-03-08 22:58:37 +00:00
|
|
|
public static GeneratorPlanet[] GenerateEntities(ZoneGenerationSettings settings, ref Random random, float mass, float radius, float2 fixedPosition)
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2020-11-27 22:47:33 +00:00
|
|
|
var root = new GeneratorPlanet
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2021-03-08 22:58:37 +00:00
|
|
|
FixedPosition = fixedPosition,
|
2020-12-01 00:16:11 +00:00
|
|
|
Settings = settings,
|
2021-03-08 22:58:37 +00:00
|
|
|
Mass = mass,
|
|
|
|
|
ChildDistanceMaximum = radius * .75f,
|
|
|
|
|
ChildDistanceMinimum = settings.PlanetSafetyRadius.Evaluate(mass)
|
2020-03-22 19:05:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// There is some chance of generating a rosette or binary system
|
2020-03-28 18:36:12 +00:00
|
|
|
// Probabilities which are fixed for the entire galaxy are in GlobalData, contained in the GameContext
|
2020-12-01 00:16:11 +00:00
|
|
|
var rosette = random.NextFloat() < settings.RosetteProbability;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
if (rosette)
|
|
|
|
|
{
|
|
|
|
|
// Create a rosette with a number of vertices between 2 and 9 inclusive
|
2020-12-01 00:16:11 +00:00
|
|
|
root.ExpandRosette(ref random, (int)(random.NextFloat(1, 5) + random.NextFloat(1, 5)));
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
// Create a small number of less massive "captured" planets orbiting past the rosette
|
|
|
|
|
root.ExpandSolar(
|
2020-12-01 00:16:11 +00:00
|
|
|
ref random,
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
count: (int)(random.NextFloat(1, 3) * random.NextFloat(1, 2)),
|
|
|
|
|
massMulMin: .6f,
|
|
|
|
|
massMulMax: .8f,
|
|
|
|
|
distMulMin: 1.25f,
|
|
|
|
|
distMulMax: 1.75f,
|
2020-03-22 19:05:26 +00:00
|
|
|
jupiterJump: 1,
|
|
|
|
|
massFraction: .1f);
|
|
|
|
|
|
|
|
|
|
var averageChildMass = root.Children.Sum(p => p.Mass) / root.Children.Count;
|
2020-12-01 00:16:11 +00:00
|
|
|
foreach(var p in root.Children.Where(c=>c.Mass>settings.GasGiantMass))
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
|
|
|
|
var m = p.Mass / averageChildMass;
|
|
|
|
|
// Give each child in the rosette its own mini solar system
|
|
|
|
|
p.ExpandSolar(
|
2020-12-01 00:16:11 +00:00
|
|
|
ref random,
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
count: (int) (random.NextFloat(1, 3 * m) + random.NextFloat(1, 3 * m)),
|
|
|
|
|
massMulMin: 0.75f,
|
|
|
|
|
massMulMax: 2.5f,
|
2020-03-30 03:11:57 +01:00
|
|
|
distMulMin: 1 + m * .25f,
|
|
|
|
|
distMulMax: 1.05f + m * .5f,
|
2020-03-22 19:05:26 +00:00
|
|
|
jupiterJump: random.NextFloat() * random.NextFloat() * 10 + 1,
|
|
|
|
|
massFraction: .5f
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Create a regular old boring solar system
|
|
|
|
|
root.ExpandSolar(
|
2020-12-01 00:16:11 +00:00
|
|
|
ref random,
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
count: random.NextInt(5, 15),
|
|
|
|
|
massMulMin: 0.75f,
|
|
|
|
|
massMulMax: 2.5f,
|
2020-03-22 19:05:26 +00:00
|
|
|
distMulMin: 1.1f,
|
2020-03-30 03:11:57 +01:00
|
|
|
distMulMax: 1.25f,
|
2020-03-22 19:05:26 +00:00
|
|
|
jupiterJump: random.NextFloat() * random.NextFloat() * 10 + 1,
|
|
|
|
|
massFraction: .25f
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 00:16:11 +00:00
|
|
|
var alreadyExpanded = new List<GeneratorPlanet>();
|
2020-11-27 22:47:33 +00:00
|
|
|
var binaries = new List<GeneratorPlanet>();
|
2020-12-01 00:16:11 +00:00
|
|
|
for(int i=0; i<settings.SatellitePasses; i++)
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2020-12-01 00:16:11 +00:00
|
|
|
// Get all children that are above the satellite creation mass floor and not rosette members
|
|
|
|
|
var satelliteCandidates = rosette
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
? root.AllPlanets().Where(p =>
|
|
|
|
|
p != root &&
|
|
|
|
|
p.Parent != root &&
|
2020-12-01 00:16:11 +00:00
|
|
|
p.Mass > settings.SatelliteCreationMassFloor &&
|
|
|
|
|
!alreadyExpanded.Contains(p))
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
: root.AllPlanets().Where(p =>
|
|
|
|
|
p != root &&
|
2020-12-01 00:16:11 +00:00
|
|
|
p.Mass > settings.SatelliteCreationMassFloor &&
|
|
|
|
|
!alreadyExpanded.Contains(p));
|
|
|
|
|
|
|
|
|
|
foreach (var planet in satelliteCandidates)
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2020-12-01 00:16:11 +00:00
|
|
|
// There's a chance of generating satellites for each qualified planet
|
|
|
|
|
if (random.NextFloat() < settings.SatelliteCreationProbability)
|
2020-03-30 17:54:54 +01:00
|
|
|
{
|
2020-12-01 00:16:11 +00:00
|
|
|
// Sometimes the satellite is so massive that it forms a binary system (like Earth!)
|
|
|
|
|
if (random.NextFloat() < settings.BinaryCreationProbability)
|
|
|
|
|
{
|
|
|
|
|
planet.ExpandRosette(ref random, 2);
|
|
|
|
|
binaries.AddRange(planet.Children);
|
|
|
|
|
}
|
|
|
|
|
// Otherwise, terrestrial planets get a couple satellites while gas giants get many
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
planet.ExpandSolar(
|
|
|
|
|
ref random,
|
|
|
|
|
count: planet.Mass < settings.GasGiantMass ? random.NextInt(1, 3) : random.NextInt(2, 6),
|
|
|
|
|
massMulMin: .75f,
|
|
|
|
|
massMulMax: 1.5f,
|
|
|
|
|
distMulMin: 1.05f,
|
|
|
|
|
distMulMax: 1.25f,
|
|
|
|
|
jupiterJump: 1,
|
|
|
|
|
massFraction: .15f); // Planetary satellites are not nearly as massive as planets themselves
|
|
|
|
|
}
|
|
|
|
|
alreadyExpanded.Add(planet);
|
2020-03-30 17:54:54 +01:00
|
|
|
}
|
2020-03-22 19:05:26 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-30 17:54:54 +01:00
|
|
|
// Get all children that are below the belt creation mass floor and not rosette members, also exclude binaries
|
|
|
|
|
var beltCandidates = rosette
|
2020-12-05 23:43:02 +00:00
|
|
|
? root.AllPlanets().Where(p => p != root && p.Parent != root && p.Mass < settings.BeltMassCeiling && !binaries.Contains(p) && p.Children.Count == 0)
|
|
|
|
|
: root.AllPlanets().Where(p => p != root && p.Mass < settings.BeltMassCeiling && !binaries.Contains(p) && p.Children.Count == 0);
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-31 13:44:18 +01:00
|
|
|
foreach(var planet in beltCandidates.Reverse())
|
2020-12-01 00:16:11 +00:00
|
|
|
if (random.NextFloat() < settings.BeltProbability && !planet.Parent.Children.Any(p=>p.Belt))
|
2020-03-30 17:54:54 +01:00
|
|
|
planet.Belt = true;
|
|
|
|
|
|
2020-12-01 00:16:11 +00:00
|
|
|
var totalMass = root.AllPlanets().Sum(p => p.Mass);
|
|
|
|
|
var rootMass = root.Mass;
|
|
|
|
|
foreach (var planet in root.AllPlanets())
|
|
|
|
|
planet.Mass = (planet.Mass / totalMass) * rootMass;
|
|
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
return root.AllPlanets().ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 18:36:12 +00:00
|
|
|
// All orbiting bodies are referred to here as "Planets"
|
|
|
|
|
// This goes back to the original etymology for the word planet, which is traveler,
|
|
|
|
|
// Because they were objects we observed to move in the sky instead of remaining fixed like the stars
|
2020-11-27 22:47:33 +00:00
|
|
|
public class GeneratorPlanet
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2020-12-01 00:16:11 +00:00
|
|
|
public ZoneGenerationSettings Settings;
|
2020-03-22 19:05:26 +00:00
|
|
|
public float Distance;
|
|
|
|
|
public float Phase;
|
|
|
|
|
public float Mass;
|
2020-12-01 00:16:11 +00:00
|
|
|
//public float Period;
|
2020-03-22 19:05:26 +00:00
|
|
|
public float ChildDistanceMinimum;
|
|
|
|
|
public float ChildDistanceMaximum;
|
|
|
|
|
public bool Empty = false;
|
2020-03-30 17:54:54 +01:00
|
|
|
public bool Belt = false;
|
2020-11-27 22:47:33 +00:00
|
|
|
public List<GeneratorPlanet> Children = new List<GeneratorPlanet>(); // Planets orbiting this one are referred to as children
|
|
|
|
|
public GeneratorPlanet Parent;
|
2021-03-08 22:58:37 +00:00
|
|
|
public float2 FixedPosition;
|
2020-03-22 19:05:26 +00:00
|
|
|
|
2020-03-28 18:36:12 +00:00
|
|
|
// Recursively gather all planets in the hierarchy
|
2020-11-27 22:47:33 +00:00
|
|
|
public IEnumerable<GeneratorPlanet> AllPlanets()
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
|
|
|
|
return new[]{this}.Concat(Children.SelectMany(c=>c.AllPlanets()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create children that fill a single orbit, equally spaced
|
|
|
|
|
// https://en.wikipedia.org/wiki/Klemperer_rosette
|
2020-12-01 00:16:11 +00:00
|
|
|
public void ExpandRosette(ref Random random, int vertices)
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2020-03-30 17:54:54 +01:00
|
|
|
//Debug.Log("Expanding Rosette");
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
// Rosette children replace the parent, parent orbital node is left empty
|
|
|
|
|
Empty = true;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
// Masses in a rosette alternate, so every sequential pair has the same shared mass
|
|
|
|
|
var sharedMass = Mass / vertices * 2;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
// Proportion of the masses of sequential pairs is random, but only for even vertex counts
|
2020-12-01 00:16:11 +00:00
|
|
|
var proportion = vertices % 2 == 0 ? random.NextFloat(.5f,.95f) : .5f;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
// Place children at a fixed distance in the center of the range
|
|
|
|
|
var dist = (ChildDistanceMinimum + ChildDistanceMaximum) / 2;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
// Position of first child
|
|
|
|
|
var p0 = new float2(0, dist);
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
// Position of second child
|
2026-08-10 14:57:50 +01:00
|
|
|
var p1 = OrbitData.Evaluate(1.0f / vertices) * dist;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
// Maximum child distance is half the distance to the neighbor minus the neighbor's radius
|
2026-08-10 14:57:50 +01:00
|
|
|
var p0ChildDist = (distance(p0, p1) * proportion - Settings.PlanetSafetyRadius.Evaluate(sharedMass * (1 - proportion))) * .75f;
|
|
|
|
|
var p1ChildDist = (distance(p0, p1) * (1 - proportion) - Settings.PlanetSafetyRadius.Evaluate(sharedMass * proportion)) * .75f;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
for (int i = 0; i < vertices; i++)
|
|
|
|
|
{
|
2020-11-27 22:47:33 +00:00
|
|
|
var child = new GeneratorPlanet
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2020-12-01 00:16:11 +00:00
|
|
|
Settings = Settings,
|
2020-03-22 19:05:26 +00:00
|
|
|
Parent = this,
|
|
|
|
|
Mass = sharedMass * (i % 2 == 0 ? proportion : 1 - proportion), // Masses alternate
|
|
|
|
|
Distance = dist,
|
|
|
|
|
Phase = (float) i / vertices,
|
|
|
|
|
ChildDistanceMaximum = (i % 2 == 0 ? p0ChildDist : p1ChildDist)
|
|
|
|
|
};
|
2020-12-01 00:16:11 +00:00
|
|
|
child.ChildDistanceMinimum = Settings.PlanetSafetyRadius.Evaluate(child.Mass) * 2;
|
|
|
|
|
//child.Period = Settings..Evaluate(child.Distance);
|
2020-03-22 19:05:26 +00:00
|
|
|
Children.Add(child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChildDistanceMinimum = dist + p0ChildDist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create children that mimic the distribution of planetary masses in the solar system
|
2020-12-01 00:16:11 +00:00
|
|
|
public void ExpandSolar(ref Random random, int count, float massMulMin, float massMulMax, float distMulMin, float distMulMax, float jupiterJump, float massFraction)
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2020-03-30 17:54:54 +01:00
|
|
|
//Debug.Log("Expanding Solar");
|
2020-03-22 19:05:26 +00:00
|
|
|
// Expansion is impossible when child space is full
|
|
|
|
|
if (count == 0 || ChildDistanceMaximum < ChildDistanceMinimum)
|
|
|
|
|
return;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
var masses = new float[count];
|
|
|
|
|
var distances = new float[count];
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
|
|
|
|
// Accumulate total mass
|
2020-03-22 19:05:26 +00:00
|
|
|
// Initialize first mass and distance, actual number doesn't matter since total mass will be divided proportionally
|
|
|
|
|
float massTotal = distances[0] = masses[0] = 1;
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
// Masses and distances multiply from one planet to the next
|
|
|
|
|
// Mass typically increases exponentially as you go further out
|
|
|
|
|
// jupiterJump is an additional mass multiplier applied after half of the planets
|
|
|
|
|
for (var i = 1; i < count; i++)
|
|
|
|
|
{
|
2020-12-01 00:16:11 +00:00
|
|
|
massTotal += masses[i] = masses[i - 1] * random.NextFloat(massMulMin, massMulMax) * (count/2==i ? jupiterJump : 1);
|
|
|
|
|
distances[i] = distances[i - 1] * random.NextFloat(distMulMin, distMulMax);
|
2020-03-22 19:05:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add some randomness to child masses
|
|
|
|
|
for (var i = 0; i < masses.Length; i++)
|
2020-12-01 00:16:11 +00:00
|
|
|
masses[i] *= random.NextFloat(.1f, 1f);
|
2020-03-22 19:05:26 +00:00
|
|
|
|
|
|
|
|
// Normalize the masses of the children
|
|
|
|
|
for (var i = 0; i < count; i++)
|
|
|
|
|
masses[i] = masses[i] / massTotal * Mass * massFraction;
|
|
|
|
|
|
|
|
|
|
// Map child distances to range between minimum and maximum
|
2020-03-29 04:19:24 +01:00
|
|
|
if (count > 1)
|
|
|
|
|
{
|
|
|
|
|
var oldDistances = (float[]) distances.Clone();
|
|
|
|
|
for (var i = 0; i < count; i++)
|
2026-08-10 14:57:50 +01:00
|
|
|
distances[i] = lerp(ChildDistanceMinimum, ChildDistanceMaximum,
|
2020-12-01 00:16:11 +00:00
|
|
|
(oldDistances[i] - oldDistances[0]) / (oldDistances[count - 1] - oldDistances[0])) + Settings.PlanetSafetyRadius.Evaluate(masses[i]);
|
2020-03-29 04:19:24 +01:00
|
|
|
}
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
|
2020-03-22 19:05:26 +00:00
|
|
|
for (var i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
// Only instantiate children above the mass floor
|
2020-12-01 00:16:11 +00:00
|
|
|
if (masses[i] > Settings.MassFloor)
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2020-11-27 22:47:33 +00:00
|
|
|
var child = new GeneratorPlanet
|
2020-03-22 19:05:26 +00:00
|
|
|
{
|
2020-12-01 00:16:11 +00:00
|
|
|
Settings = Settings,
|
2020-03-22 19:05:26 +00:00
|
|
|
Parent = this,
|
|
|
|
|
Mass = masses[i],
|
|
|
|
|
Distance = distances[i],
|
2020-12-01 00:16:11 +00:00
|
|
|
Phase = random.NextFloat()
|
2020-03-22 19:05:26 +00:00
|
|
|
};
|
2020-12-01 00:16:11 +00:00
|
|
|
//child.Period = Context.GlobalData.OrbitalPeriod(child.Distance);
|
|
|
|
|
child.ChildDistanceMinimum = Settings.PlanetSafetyRadius.Evaluate(child.Mass) * 2;
|
2020-03-22 19:05:26 +00:00
|
|
|
// Maximum child distance of child is the smallest distance to either of its neighbors
|
2026-08-10 14:57:50 +01:00
|
|
|
child.ChildDistanceMaximum = min(i == 0 ? child.Distance - ChildDistanceMinimum : child.Distance - distances[i - 1],
|
2020-03-22 19:05:26 +00:00
|
|
|
i < count - 1 ? distances[i + 1] - child.Distance : float.PositiveInfinity);
|
|
|
|
|
if (float.IsNaN(child.Distance))
|
|
|
|
|
throw new NotFiniteNumberException($"Planet created with NaN distance, something went very wrong!");
|
|
|
|
|
if (child.ChildDistanceMaximum > child.ChildDistanceMinimum)
|
|
|
|
|
Children.Add(child);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
Move the ServerShared data model onto CultLib's CultCache
Cut 8b. Aetheria.Shared builds headless against GameCult.Caching and
GameCult.Caching.MessagePack at the pinned CultLib; the pin guard also
checks the Caching project exists.
Documents: [CultDocument("aetheria.<type>", "1")] with [MessagePackObject]
and integer [Key]s on the Q7 concrete types (the eight ItemData leaves,
Faction, FactionProductData, PersonalityAttribute, NameFile, OrbitData, the
four BodyData leaves, SavedZone, SavedGame, PlayerSettings, InputLayout).
SavedGame and PlayerSettings are [CultGlobal]; Name members are [CultName].
ItemData, CraftedItemData, EquippableItemData, BodyData and AgentTask lose
[MessagePackObject]; BodyData and AgentTask lose their [Union]s. InputLayout
moves into ServerShared so AetheriaStores can route it.
References: Guid and DatabaseLink<T> members become CultRecordRef<T>;
Guid-keyed maps become ref-keyed with [CultReference(..., many: true)];
ZonePack holds refs to orbit and body records. Runtime maps (Zone orbits,
bodies and belts, cargo ItemsOfType, agent targets) key by CultRecordKey.
Identity is the record key: ID comparisons become reference or key
comparisons. StatModifierData.RequireBehavior is a simple type name.
AetheriaStores.Open composes the catalog (read-only unless writable), run
and player stores, one home per type. CultRecordRefs carries IsSet, a
null-safe Get(ref), RefOf (key from TryGetHandle) and Upsert.
ItemManager.GetData is the one item resolution path. MathResolver is
declared by [assembly: CultCacheFormatterResolver].
Inspector attributes: text, range and asset path map onto CultLib's
CultInspector attributes (asset path only on string members); every
replaced Aetheria attribute keeps [Inspectable] for the runtime panel.
Builds: Aetheria.Shared. Does not build: AetherDb and the Unity project,
which follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:00:42 +02:00
|
|
|
}
|