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
|
2026-08-10 14:57:50 +01: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;
|
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using System.Collections.Generic;
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
using System.Linq;
|
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
|
|
|
|
using GameCult.Caching;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
using MessagePack;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using UniRx;
|
2026-09-14 17:39:12 +02:00
|
|
|
|
using CultMath;
|
|
|
|
|
|
using static CultMath.math;
|
|
|
|
|
|
using float2 = CultMath.float2;
|
|
|
|
|
|
using float3 = CultMath.float3;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public class ZonePack
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("planets"), Key(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
|
|
|
|
public List<CultRecordRef<BodyData>> Planets = new List<CultRecordRef<BodyData>>();
|
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
[JsonProperty("orbits"), Key(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
|
|
|
|
public List<CultRecordRef<OrbitData>> Orbits = new List<CultRecordRef<OrbitData>>();
|
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
[JsonProperty("entities"), Key(2)]
|
|
|
|
|
|
public List<EntityPack> Entities = new List<EntityPack>();
|
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
|
|
|
|
[JsonProperty("radius"), Key(3)]
|
|
|
|
|
|
public float Radius = 2000;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("mass"), Key(4)]
|
|
|
|
|
|
public float Mass = 10000;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("time"), Key(5)]
|
|
|
|
|
|
public double Time;
|
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
|
|
|
|
|
Cut 2: the dead mining path goes, and chunks get one owner
MiningTool (its Execute range-checked against nothing, since no writer ever
set AsteroidBelt/Asteroid) and Zone.MineAsteroid (mints nothing; the
SimpleCommodity drop was commented out) are deleted, per Q1=A: "Bring back
the drill, indeed." Union tag 26 is retired, not reused. ResourceScanner's
dead survey Execute and its scan-target fields go too; its authored Range/
MinimumDensity/ScanDuration stay pending Q2. PlanetSettings.MiningDifficulty
had one reader, now gone.
Chunk wear (damage, broken-until) moves off AsteroidBelt's three per-belt
dictionaries and onto Zone as the one owner (Q3=A): Zone.Wear is the only
writer, ChunkExists/ChunkRadius are the readers, and respawn is a lazy
comparison against zone time, never a per-tick loop. ChunkId is
(CultRecordKey Field, int Index) -- "Field", not "Belt", because chunks are
meant to generalize past asteroid belts to other debris fields later.
Persisted in ZonePack's new key 6 (List<ChunkWearPack>, nullable so an older
record with no key 6 loads with no wear instead of throwing), pruned of
healed, non-broken entries at pack.
The break threshold stays strictly greater-than, matching today's behaviour:
damage exactly equal to hitpoints does not yet break the chunk.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 11:30:44 +02:00
|
|
|
|
// Cut 2 (docs/mining-cut.md): chunk wear (damage taken, broken-until time), keyed by chunk. Nullable, not
|
|
|
|
|
|
// defaulted to an empty list: an older record has no key 6 at all, and MessagePack fills a missing trailing
|
|
|
|
|
|
// key with nil, which a reference-typed field reads back as null. ARunWithoutKeySixLoadsWhole pins that a
|
|
|
|
|
|
// record from before this key existed loads with no wear, rather than throwing or silently reading a
|
|
|
|
|
|
// default instance.
|
|
|
|
|
|
[JsonProperty("chunkWear"), Key(6)]
|
|
|
|
|
|
public List<ChunkWearPack> ChunkWear;
|
2026-10-07 06:41:33 +02:00
|
|
|
|
|
|
|
|
|
|
// True once this zone's boss has been spawned. The dead boss leaves no entity, so this is the one fact that tells a
|
|
|
|
|
|
// slain boss from one that never existed; RunGoal.ExitOpen reads it. Written only by ZoneGenerator, carried by PackZone.
|
|
|
|
|
|
[JsonProperty("bossSpawned"), Key(7)]
|
|
|
|
|
|
public bool BossSpawned;
|
Cut 2: the dead mining path goes, and chunks get one owner
MiningTool (its Execute range-checked against nothing, since no writer ever
set AsteroidBelt/Asteroid) and Zone.MineAsteroid (mints nothing; the
SimpleCommodity drop was commented out) are deleted, per Q1=A: "Bring back
the drill, indeed." Union tag 26 is retired, not reused. ResourceScanner's
dead survey Execute and its scan-target fields go too; its authored Range/
MinimumDensity/ScanDuration stay pending Q2. PlanetSettings.MiningDifficulty
had one reader, now gone.
Chunk wear (damage, broken-until) moves off AsteroidBelt's three per-belt
dictionaries and onto Zone as the one owner (Q3=A): Zone.Wear is the only
writer, ChunkExists/ChunkRadius are the readers, and respawn is a lazy
comparison against zone time, never a per-tick loop. ChunkId is
(CultRecordKey Field, int Index) -- "Field", not "Belt", because chunks are
meant to generalize past asteroid belts to other debris fields later.
Persisted in ZonePack's new key 6 (List<ChunkWearPack>, nullable so an older
record with no key 6 loads with no wear instead of throwing), pruned of
healed, non-broken entries at pack.
The break threshold stays strictly greater-than, matching today's behaviour:
damage exactly equal to hitpoints does not yet break the chunk.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 11:30:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Cut 2 (docs/mining-cut.md): one packed wear entry for one chunk. "Field" names the body that owns the chunk
|
|
|
|
|
|
// (an asteroid belt today; the operator's ruling generalizes chunks to other kinds of debris field later), not
|
|
|
|
|
|
// "belt" -- the chunk surface itself must not bake in the one live field kind.
|
|
|
|
|
|
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public class ChunkWearPack
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("field"), Key(0)]
|
|
|
|
|
|
public CultRecordKey Field;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("index"), Key(1)]
|
|
|
|
|
|
public int Index;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("damage"), Key(2)]
|
|
|
|
|
|
public float Damage;
|
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
|
|
|
|
|
Cut 2: the dead mining path goes, and chunks get one owner
MiningTool (its Execute range-checked against nothing, since no writer ever
set AsteroidBelt/Asteroid) and Zone.MineAsteroid (mints nothing; the
SimpleCommodity drop was commented out) are deleted, per Q1=A: "Bring back
the drill, indeed." Union tag 26 is retired, not reused. ResourceScanner's
dead survey Execute and its scan-target fields go too; its authored Range/
MinimumDensity/ScanDuration stay pending Q2. PlanetSettings.MiningDifficulty
had one reader, now gone.
Chunk wear (damage, broken-until) moves off AsteroidBelt's three per-belt
dictionaries and onto Zone as the one owner (Q3=A): Zone.Wear is the only
writer, ChunkExists/ChunkRadius are the readers, and respawn is a lazy
comparison against zone time, never a per-tick loop. ChunkId is
(CultRecordKey Field, int Index) -- "Field", not "Belt", because chunks are
meant to generalize past asteroid belts to other debris fields later.
Persisted in ZonePack's new key 6 (List<ChunkWearPack>, nullable so an older
record with no key 6 loads with no wear instead of throwing), pruned of
healed, non-broken entries at pack.
The break threshold stays strictly greater-than, matching today's behaviour:
damage exactly equal to hitpoints does not yet break the chunk.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 11:30:44 +02:00
|
|
|
|
[JsonProperty("brokenUntil"), Key(3)]
|
|
|
|
|
|
public double? BrokenUntil;
|
2026-08-10 14:57:50 +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
|
|
|
|
[JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public abstract class BodyData
|
2026-08-10 14:57:50 +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
|
|
|
|
[CultName, JsonProperty("name"), Key(1)]
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public string Name = "";
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("orbit"), Key(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
|
|
|
|
public CultRecordRef<OrbitData> Orbit;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
[JsonProperty("mass"), Key(3)]
|
|
|
|
|
|
public float Mass = 0;
|
|
|
|
|
|
|
2026-10-27 06:34:12 +01:00
|
|
|
|
// MessagePack key 4 belonged to the removed stored Resources; do not reuse it. A belt's yield is derived by FieldKinds.Yield.
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
[JsonProperty("bodyRadiusMul")] [Key(5)]
|
|
|
|
|
|
public float BodyRadiusMultiplier = 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
|
|
|
|
[CultDocument("aetheria.planetdata", "1"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public class PlanetData : BodyData
|
|
|
|
|
|
{
|
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
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
[CultDocument("aetheria.asteroidbeltdata", "1"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public class AsteroidBeltData : BodyData
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("asteroids"), Key(9)]
|
|
|
|
|
|
public Asteroid[] Asteroids;
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
|
|
|
|
|
|
// Mining Cut 3 (docs/mining-cut-refresh.md, Q16 B): the belt's field kind, chosen once and saved. Unset on a
|
|
|
|
|
|
// belt from before this key existed (MessagePack leaves a missing trailing key at its default); Zone assigns
|
|
|
|
|
|
// it on first load through FieldKinds.Ensure, the same function generation uses.
|
|
|
|
|
|
[JsonProperty("kind"), Key(10)]
|
|
|
|
|
|
public CultRecordRef<FieldKindData> Kind;
|
2026-10-18 16:28:42 +02:00
|
|
|
|
|
|
|
|
|
|
// A Trojan swarm (ruling trojans-ride-host-period-with-stations): every rock rides the belt's rail at the rail's period, so
|
|
|
|
|
|
// the swarm keeps its shape. False on a belt from before this key (MessagePack leaves a missing trailing key at its default),
|
|
|
|
|
|
// which keeps each rock on its own distance's period: an annulus.
|
|
|
|
|
|
[JsonProperty("ridesRail"), Key(11)]
|
|
|
|
|
|
public bool RidesRail;
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Mining Cut 3 (docs/mining-cut-refresh.md, Q11): a kind of debris field, an authored catalog record. A chunk's
|
2026-10-27 06:42:58 +01:00
|
|
|
|
// reflectivity comes from its field's kind; its yields (ore geography, ruling ores-are-regional) are the ores a belt of
|
|
|
|
|
|
// this kind holds before a province weights them. FieldKinds.Yield is the one reader.
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
[CultDocument("aetheria.fieldkinddata", "1"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public class FieldKindData
|
|
|
|
|
|
{
|
|
|
|
|
|
[CultName, JsonProperty("name"), Key(0)]
|
|
|
|
|
|
public string Name = "";
|
|
|
|
|
|
|
|
|
|
|
|
// Reflectivity per schematic cell of chunk area: a chunk reflects CrossSection × its area in cells × the light
|
|
|
|
|
|
// on it (Zone.ChunkVisibility), the rule Reflector applies to a hull.
|
|
|
|
|
|
[JsonProperty("crossSection"), Key(1)]
|
|
|
|
|
|
public float CrossSection;
|
|
|
|
|
|
|
|
|
|
|
|
// How often generation picks this kind, relative to the others (FieldKinds.Assign). Zero never picks it.
|
|
|
|
|
|
[JsonProperty("generationWeight"), Key(2)]
|
|
|
|
|
|
public float GenerationWeight = 1;
|
2026-10-27 06:42:58 +01:00
|
|
|
|
|
|
|
|
|
|
// The ores a belt of this kind holds. Empty on a kind from before this key (MessagePack leaves a missing trailing key at its default).
|
|
|
|
|
|
[JsonProperty("yields"), Key(3)]
|
|
|
|
|
|
public List<FieldYield> Yields = new List<FieldYield>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// One ore a kind of field holds: Abundance of Commodity before the belt's province weights it. A Regional ore is held only
|
|
|
|
|
|
// where the province lists it (ruling ores-are-regional: some ores exist only in some provinces); any other entry is held
|
|
|
|
|
|
// everywhere, scaled by the province's factor for it.
|
|
|
|
|
|
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public class FieldYield
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("commodity"), Key(0)]
|
|
|
|
|
|
public CultRecordRef<SimpleCommodityData> Commodity;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("abundance"), Key(1)]
|
|
|
|
|
|
public float Abundance;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("regional"), Key(2)]
|
|
|
|
|
|
public bool Regional;
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-27 06:42:58 +01:00
|
|
|
|
// Ore geography (rulings provinces-within-regions, ores-are-regional): a kind of ground, authored. A region deals its
|
|
|
|
|
|
// provinces' types from a deck holding GenerationWeight cards of each type (ruling province-types-dealt-per-region);
|
|
|
|
|
|
// a province's type weights which kinds its belts draw and which ores a kind holds there.
|
|
|
|
|
|
[CultDocument("aetheria.provincedata", "1"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public class ProvinceData
|
|
|
|
|
|
{
|
|
|
|
|
|
[CultName, JsonProperty("name"), Key(0)]
|
|
|
|
|
|
public string Name = "";
|
|
|
|
|
|
|
|
|
|
|
|
// The number of cards of this type in a region's deck. Zero is never dealt.
|
|
|
|
|
|
[JsonProperty("generationWeight"), Key(1)]
|
|
|
|
|
|
public int GenerationWeight = 1;
|
|
|
|
|
|
|
|
|
|
|
|
// Multiplies the kind's own GenerationWeight in a belt's draw; a kind not listed is multiplied by one.
|
|
|
|
|
|
[JsonProperty("kinds"), Key(2)]
|
|
|
|
|
|
public List<ProvinceKindWeight> Kinds = new List<ProvinceKindWeight>();
|
|
|
|
|
|
|
|
|
|
|
|
// Multiplies an ore's Abundance wherever it is held; an ore not listed is multiplied by one, or by zero when the
|
|
|
|
|
|
// kind's entry for it is Regional.
|
|
|
|
|
|
[JsonProperty("ores"), Key(3)]
|
|
|
|
|
|
public List<ProvinceOre> Ores = new List<ProvinceOre>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public class ProvinceKindWeight
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("kind"), Key(0)]
|
|
|
|
|
|
public CultRecordRef<FieldKindData> Kind;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("weight"), Key(1)]
|
|
|
|
|
|
public float Weight = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public class ProvinceOre
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("commodity"), Key(0)]
|
|
|
|
|
|
public CultRecordRef<SimpleCommodityData> Commodity;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("weight"), Key(1)]
|
|
|
|
|
|
public float Weight = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Mining Cut 3 (Q16 B): the one owner of which kind a belt is. Ore geography: and of what it yields.
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
public static class FieldKinds
|
|
|
|
|
|
{
|
2026-10-27 06:42:58 +01:00
|
|
|
|
// The one weighted pick: an index into weights, chosen by a stable hash of the site (the same site and the same
|
|
|
|
|
|
// weights always give the same index), never one whose weight is not positive. -1 when no weight is positive.
|
|
|
|
|
|
internal static int Pick(string site, double[] weights)
|
|
|
|
|
|
{
|
|
|
|
|
|
var pick = site.StableHash() / 4294967296.0 * weights.Sum();
|
|
|
|
|
|
var last = -1;
|
|
|
|
|
|
for (var i = 0; i < weights.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (weights[i] <= 0) continue;
|
|
|
|
|
|
last = i;
|
|
|
|
|
|
if (pick < weights[i]) return i;
|
|
|
|
|
|
pick -= weights[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
return last;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// A weighted pick among the catalog's kinds, by a stable hash of the belt's site: the same site, catalog and province
|
|
|
|
|
|
// type always give the same kind. A kind's weight is its GenerationWeight times its province's factor for it; a belt with
|
|
|
|
|
|
// no province type (unset, or naming nothing the catalog holds) draws unweighted. Generation names a belt by the zone's
|
|
|
|
|
|
// seed and the belt's place in generation order (never by the key the cache mints, which differs every run); a belt saved
|
|
|
|
|
|
// without a kind is named by its key. Kinds are ordered by key so the pick does not depend on store order.
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
// Unset when the catalog has no kind with positive weight.
|
2026-10-27 06:42:58 +01:00
|
|
|
|
public static CultRecordRef<FieldKindData> Assign(string site, CultCache catalog, CultRecordRef<ProvinceData> province)
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
{
|
2026-10-27 06:42:58 +01:00
|
|
|
|
var type = catalog.Get(province);
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
var kinds = catalog.GetAll<FieldKindData>()
|
2026-10-27 06:42:58 +01:00
|
|
|
|
.Select(kind =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var key = catalog.RefOf(kind).Key;
|
2026-10-27 09:59:15 +01:00
|
|
|
|
var factor = Factor(type?.Kinds.Where(entry => entry.Kind.Key.Equals(key)).Select(entry => (double) entry.Weight), 1.0);
|
|
|
|
|
|
// Both must be positive: two negatives do not multiply into a weight.
|
|
|
|
|
|
return (Key: key, Weight: kind.GenerationWeight > 0f && factor > 0.0 ? kind.GenerationWeight * factor : 0.0);
|
2026-10-27 06:42:58 +01:00
|
|
|
|
})
|
|
|
|
|
|
.Where(kind => kind.Weight > 0.0)
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
.OrderBy(kind => kind.Key.Value, StringComparer.Ordinal)
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
if (kinds.Length == 0) return default;
|
|
|
|
|
|
|
2026-10-27 06:42:58 +01:00
|
|
|
|
return new CultRecordRef<FieldKindData>(kinds[Pick(site, kinds.Select(kind => kind.Weight).ToArray())].Key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// A province's factor for one entry: the sum of its listings, or the default when it lists none.
|
|
|
|
|
|
private static double Factor(IEnumerable<double> listed, double unlisted)
|
|
|
|
|
|
{
|
|
|
|
|
|
var weights = listed?.ToArray();
|
|
|
|
|
|
return weights == null || weights.Length == 0 ? unlisted : weights.Sum();
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Gives a belt with no kind its kind and saves it; a belt that has one keeps it, whatever the catalog now says.
|
|
|
|
|
|
// Generation (ZoneGenerator) and first load (Zone) both call this, so there is one assignment rule.
|
2026-10-27 06:42:58 +01:00
|
|
|
|
public static void Ensure(string site, AsteroidBeltData belt, CultCache cache, CultRecordRef<ProvinceData> province)
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (belt.Kind.IsSet()) return;
|
2026-10-27 06:42:58 +01:00
|
|
|
|
belt.Kind = Assign(site, cache, province);
|
Mining Cut 3: field kinds, chunk visibility, the chunk query, and TargetRef
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:24 +02:00
|
|
|
|
if (belt.Kind.IsSet()) cache.Upsert(belt);
|
|
|
|
|
|
}
|
2026-10-27 06:42:58 +01:00
|
|
|
|
|
|
|
|
|
|
// What a belt of this kind holds in a zone of this province: each of the kind's yields, its Abundance times the
|
|
|
|
|
|
// province's factor for the ore (unlisted: one, or zero when the yield is Regional), summed per commodity, in commodity
|
|
|
|
|
|
// key order, zero amounts dropped. Derived on every call, never stored. Empty for an unset kind.
|
|
|
|
|
|
public static (CultRecordRef<SimpleCommodityData> Commodity, float Amount)[] Yield(CultRecordRef<FieldKindData> kind,
|
|
|
|
|
|
CultRecordRef<ProvinceData> province, CultCache catalog)
|
|
|
|
|
|
{
|
|
|
|
|
|
var data = catalog.Get(kind);
|
|
|
|
|
|
if (data == null) return Array.Empty<(CultRecordRef<SimpleCommodityData>, float)>();
|
|
|
|
|
|
var type = catalog.Get(province);
|
|
|
|
|
|
return data.Yields
|
|
|
|
|
|
.Select(yield => (yield.Commodity, Amount: yield.Abundance * Factor(
|
|
|
|
|
|
type?.Ores.Where(ore => ore.Commodity.Key.Equals(yield.Commodity.Key)).Select(ore => (double) ore.Weight), yield.Regional ? 0.0 : 1.0)))
|
|
|
|
|
|
.GroupBy(yield => yield.Commodity.Key.Value, StringComparer.Ordinal)
|
|
|
|
|
|
.OrderBy(group => group.Key, StringComparer.Ordinal)
|
|
|
|
|
|
.Select(group => (group.First().Commodity, Amount: (float) group.Sum(yield => yield.Amount)))
|
|
|
|
|
|
.Where(yield => yield.Amount > 0f)
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Ore geography (rulings provinces-within-regions, boss-zone-is-near-side, province-partition-by-size, province-types-dealt-per-region).
|
|
|
|
|
|
// Pure functions of the zone graph, the run seed and the catalog: nothing here draws from Galaxy's Random stream, stores membership
|
|
|
|
|
|
// or reads who owns what. Galaxy builds provinces once, in its generation and save constructors, and nothing re-partitions them.
|
|
|
|
|
|
public static class Provinces
|
|
|
|
|
|
{
|
|
|
|
|
|
// Zones per province when a settings object does not say (SectorGenerationSettings.ProvinceZones): calibration, not a rule.
|
|
|
|
|
|
public const int DefaultZonesPerProvince = 8;
|
|
|
|
|
|
|
2026-10-27 17:25:19 +01:00
|
|
|
|
// A boss zone sits on the near side of its border: its crossing links are its links to zones in the component of exit in
|
|
|
|
|
|
// the graph without it. The one definition, read by Regions (which cuts them) and ExtraExits (which prunes all but one).
|
|
|
|
|
|
// exit unset (-1) gives none, and a boss zone that is the exit has none. Ordered by boss, then far zone.
|
|
|
|
|
|
public static (int Boss, int Far)[] CrossingLinks(int[][] adjacency, IReadOnlyCollection<int> bossZones, int exit)
|
2026-10-27 06:42:58 +01:00
|
|
|
|
{
|
2026-10-27 17:25:19 +01:00
|
|
|
|
var links = new List<(int Boss, int Far)>();
|
2026-10-27 06:42:58 +01:00
|
|
|
|
if (exit >= 0)
|
2026-10-27 17:25:19 +01:00
|
|
|
|
foreach (var boss in bossZones.Distinct().Where(boss => boss != exit).OrderBy(boss => boss))
|
2026-10-27 06:42:58 +01:00
|
|
|
|
{
|
|
|
|
|
|
var exitSide = Flood(adjacency, exit, (a, b) => b != boss);
|
2026-10-27 17:25:19 +01:00
|
|
|
|
foreach (var next in adjacency[boss].OrderBy(next => next))
|
|
|
|
|
|
if (exitSide[next]) links.Add((boss, next));
|
2026-10-27 06:42:58 +01:00
|
|
|
|
}
|
2026-10-27 17:25:19 +01:00
|
|
|
|
return links.ToArray();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-27 17:28:08 +01:00
|
|
|
|
// The crossing links to prune so each boss zone keeps one exit: all of them but the link to the boss zone's successor on
|
2026-10-27 20:23:47 +01:00
|
|
|
|
// exitPath (the zones from the entrance to the exit), which is the link the run goes through. The generator's exit path
|
|
|
|
|
|
// passes through every boss zone by one of its crossing links; a boss zone off exitPath, last on it, or followed by a zone
|
|
|
|
|
|
// it has no crossing link to breaks that, and throws naming the boss zone rather than guessing which link to keep.
|
|
|
|
|
|
public static (int Boss, int Far)[] ExtraExits(int[][] adjacency, IReadOnlyCollection<int> bossZones, int exit, int[] exitPath)
|
|
|
|
|
|
{
|
|
|
|
|
|
var extra = new List<(int Boss, int Far)>();
|
|
|
|
|
|
foreach (var links in CrossingLinks(adjacency, bossZones, exit).GroupBy(link => link.Boss))
|
|
|
|
|
|
{
|
|
|
|
|
|
var at = Array.IndexOf(exitPath, links.Key);
|
|
|
|
|
|
var kept = at >= 0 && at + 1 < exitPath.Length ? exitPath[at + 1] : -1;
|
|
|
|
|
|
if (!links.Any(link => link.Far == kept))
|
|
|
|
|
|
throw new InvalidOperationException($"boss zone {links.Key} has no crossing link to its successor on the exit path, so no exit to keep");
|
|
|
|
|
|
extra.AddRange(links.Where(link => link.Far != kept));
|
|
|
|
|
|
}
|
|
|
|
|
|
return extra.ToArray();
|
|
|
|
|
|
}
|
2026-10-27 17:28:08 +01:00
|
|
|
|
|
2026-10-27 17:25:19 +01:00
|
|
|
|
// Each zone's region, named by the region's least zone index. Cutting every boss zone's crossing links (CrossingLinks)
|
|
|
|
|
|
// leaves the regions as the connected components. Every zone, boss zones included, is in exactly one.
|
|
|
|
|
|
public static int[] Regions(int[][] adjacency, IReadOnlyCollection<int> bossZones, int exit)
|
|
|
|
|
|
{
|
|
|
|
|
|
var cut = CrossingLinks(adjacency, bossZones, exit).Select(link => (Math.Min(link.Boss, link.Far), Math.Max(link.Boss, link.Far))).ToHashSet();
|
2026-10-27 06:42:58 +01:00
|
|
|
|
|
|
|
|
|
|
var regionOf = new int[adjacency.Length];
|
|
|
|
|
|
Array.Fill(regionOf, -1);
|
|
|
|
|
|
for (var start = 0; start < adjacency.Length; start++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (regionOf[start] >= 0) continue;
|
|
|
|
|
|
var members = Flood(adjacency, start, (a, b) => !cut.Contains((Math.Min(a, b), Math.Max(a, b))));
|
|
|
|
|
|
for (var i = 0; i < members.Length; i++) if (members[i]) regionOf[i] = start;
|
|
|
|
|
|
}
|
|
|
|
|
|
return regionOf;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Each zone's province, named by the province's seed zone index. A region of size s takes max(1, round(s / zonesPerProvince))
|
|
|
|
|
|
// seeds: the first is the zone with the least hash of the run seed, each next the zone farthest (in-region hops) from the
|
|
|
|
|
|
// seeds chosen so far, ties by that hash; then every zone joins the nearest seed, ties to the earlier seed. A province is
|
|
|
|
|
|
// contiguous and never crosses a region.
|
|
|
|
|
|
public static int[] Partition(int[][] adjacency, int[] regionOf, uint runSeed, int zonesPerProvince)
|
|
|
|
|
|
{
|
|
|
|
|
|
var seedOf = new int[adjacency.Length];
|
|
|
|
|
|
uint Hash(int index) => $"{runSeed}:province-seed:{index}".StableHash();
|
|
|
|
|
|
foreach (var region in Enumerable.Range(0, adjacency.Length).GroupBy(zone => regionOf[zone]).OrderBy(group => group.Key))
|
|
|
|
|
|
{
|
|
|
|
|
|
var zones = region.ToArray();
|
|
|
|
|
|
var count = Math.Min(zones.Length, Math.Max(1, (int) Math.Round((double) zones.Length / Math.Max(1, zonesPerProvince), MidpointRounding.AwayFromZero)));
|
|
|
|
|
|
var seeds = new List<int> { zones.OrderBy(Hash).ThenBy(zone => zone).First() };
|
|
|
|
|
|
while (seeds.Count < count)
|
|
|
|
|
|
{
|
|
|
|
|
|
var hops = Grow(adjacency, regionOf, seeds.ToArray()).Hops;
|
|
|
|
|
|
seeds.Add(zones.Where(zone => !seeds.Contains(zone)).OrderByDescending(zone => hops[zone]).ThenBy(Hash).ThenBy(zone => zone).First());
|
|
|
|
|
|
}
|
|
|
|
|
|
var owner = Grow(adjacency, regionOf, seeds.ToArray()).Owner;
|
|
|
|
|
|
foreach (var zone in zones) seedOf[zone] = seeds[owner[zone]];
|
|
|
|
|
|
}
|
|
|
|
|
|
return seedOf;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// A region's hand of province types, one per province in ascending seed-zone order. Types are dealt without replacement from
|
|
|
|
|
|
// a deck of GenerationWeight cards per type (catalog types in key order), each draw one weighted pick over the cards remaining
|
|
|
|
|
|
// at a site named by the run seed, the region and the draw's place; the deck is restored whole when it runs out. An empty
|
|
|
|
|
|
// deck deals every province an unset type.
|
|
|
|
|
|
public static CultRecordRef<ProvinceData>[] Deal(int regionKey, int seedCount, uint runSeed, CultCache catalog)
|
|
|
|
|
|
{
|
|
|
|
|
|
var types = catalog.GetAll<ProvinceData>()
|
|
|
|
|
|
.Where(type => type.GenerationWeight > 0)
|
|
|
|
|
|
.Select(type => (Ref: catalog.RefOf(type), Cards: type.GenerationWeight))
|
|
|
|
|
|
.OrderBy(type => type.Ref.Key.Value, StringComparer.Ordinal)
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
var hand = new CultRecordRef<ProvinceData>[seedCount];
|
|
|
|
|
|
if (types.Length == 0) return hand;
|
|
|
|
|
|
|
|
|
|
|
|
var remaining = types.Select(type => type.Cards).ToArray();
|
|
|
|
|
|
for (var n = 0; n < seedCount; n++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (remaining.Sum() == 0) remaining = types.Select(type => type.Cards).ToArray();
|
|
|
|
|
|
var card = FieldKinds.Pick($"{runSeed}:province-deal:{regionKey}:{n}", remaining.Select(cards => (double) cards).ToArray());
|
|
|
|
|
|
remaining[card]--;
|
|
|
|
|
|
hand[n] = types[card].Ref;
|
|
|
|
|
|
}
|
|
|
|
|
|
return hand;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Every zone reachable from start over the links the filter allows.
|
|
|
|
|
|
private static bool[] Flood(int[][] adjacency, int start, Func<int, int, bool> allowed)
|
|
|
|
|
|
{
|
|
|
|
|
|
var reached = new bool[adjacency.Length];
|
|
|
|
|
|
var stack = new Stack<int>();
|
|
|
|
|
|
reached[start] = true;
|
|
|
|
|
|
stack.Push(start);
|
|
|
|
|
|
while (stack.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
var zone = stack.Pop();
|
|
|
|
|
|
foreach (var next in adjacency[zone])
|
|
|
|
|
|
if (!reached[next] && allowed(zone, next))
|
|
|
|
|
|
{
|
|
|
|
|
|
reached[next] = true;
|
|
|
|
|
|
stack.Push(next);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return reached;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Breadth-first growth from seeds inside their regions, level by level: each zone's hops to the nearest seed and the rank
|
|
|
|
|
|
// (place in seeds) of the earliest seed among those that reach it first.
|
|
|
|
|
|
private static (int[] Owner, int[] Hops) Grow(int[][] adjacency, int[] regionOf, int[] seeds)
|
|
|
|
|
|
{
|
|
|
|
|
|
var owner = new int[adjacency.Length];
|
|
|
|
|
|
var hops = new int[adjacency.Length];
|
|
|
|
|
|
Array.Fill(owner, -1);
|
|
|
|
|
|
for (var rank = 0; rank < seeds.Length; rank++)
|
|
|
|
|
|
{
|
|
|
|
|
|
owner[seeds[rank]] = rank;
|
|
|
|
|
|
hops[seeds[rank]] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
var frontier = seeds.ToList();
|
|
|
|
|
|
for (var level = 1; frontier.Count > 0; level++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var claims = new Dictionary<int, int>();
|
|
|
|
|
|
foreach (var zone in frontier)
|
|
|
|
|
|
foreach (var next in adjacency[zone])
|
|
|
|
|
|
if (owner[next] < 0 && regionOf[next] == regionOf[zone])
|
|
|
|
|
|
claims[next] = claims.TryGetValue(next, out var rank) ? Math.Min(rank, owner[zone]) : owner[zone];
|
|
|
|
|
|
foreach (var claim in claims)
|
|
|
|
|
|
{
|
|
|
|
|
|
owner[claim.Key] = claim.Value;
|
|
|
|
|
|
hops[claim.Key] = level;
|
|
|
|
|
|
}
|
|
|
|
|
|
frontier = claims.Keys.OrderBy(zone => zone).ToList();
|
|
|
|
|
|
}
|
|
|
|
|
|
return (owner, hops);
|
|
|
|
|
|
}
|
2026-08-10 14:57:50 +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
|
|
|
|
[CultDocument("aetheria.gasgiantdata", "1"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public class GasGiantData : BodyData
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("firstOffsetDomainRotationSpeed"), Key(9)]
|
|
|
|
|
|
public float FirstOffsetDomainRotationSpeed = 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
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
[JsonProperty("firstOffsetRotationSpeed"), Key(10)]
|
|
|
|
|
|
public float FirstOffsetRotationSpeed = 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
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
[JsonProperty("secondOffsetDomainRotationSpeed"), Key(11)]
|
|
|
|
|
|
public float SecondOffsetDomainRotationSpeed = 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
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
[JsonProperty("secondOffsetRotationSpeed"), Key(12)]
|
|
|
|
|
|
public float SecondOffsetRotationSpeed = 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
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
[JsonProperty("albedoRotationSpeed"), Key(13)]
|
|
|
|
|
|
public float AlbedoRotationSpeed = 1;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("materialOverrides"), Key(18)]
|
|
|
|
|
|
public List<string> MaterialOverrides = new List<string>();
|
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
|
|
|
|
[JsonProperty("colors"), Key(19)]
|
|
|
|
|
|
public float4[] Colors = new float4[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
|
|
|
|
[CultDocument("aetheria.sundata", "1"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
2026-08-10 14:57:50 +01:00
|
|
|
|
public class SunData : GasGiantData
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("lightColor"), Key(20)]
|
|
|
|
|
|
public float3 LightColor = float3.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
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
[JsonProperty("fogTintColor"), Key(21)]
|
|
|
|
|
|
public float3 FogTintColor = float3.zero;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("lightRadiusMul")] [Key(22)]
|
|
|
|
|
|
public float LightRadiusMultiplier = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public class Asteroid
|
|
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("distance"), Key(0)]
|
|
|
|
|
|
public float 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
|
|
|
|
[JsonProperty("phase"), Key(1)]
|
|
|
|
|
|
public float 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
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
[JsonProperty("size"), Key(2)]
|
|
|
|
|
|
public float Size;
|
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
|
|
|
|
[JsonProperty("rotationSpeed"), Key(3)]
|
|
|
|
|
|
public float RotationSpeed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
[CultDocument("aetheria.orbitdata", "1"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
|
|
|
|
|
public class OrbitData
|
2026-08-10 14:57:50 +01:00
|
|
|
|
{
|
|
|
|
|
|
[JsonProperty("parent"), Key(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
|
|
|
|
public CultRecordRef<OrbitData> Parent;
|
2026-08-10 14:57:50 +01:00
|
|
|
|
|
|
|
|
|
|
[JsonProperty("distance"), Key(2)]
|
|
|
|
|
|
public float Distance;
|
|
|
|
|
|
|
|
|
|
|
|
[JsonProperty("phase"), Key(3)]
|
|
|
|
|
|
public float 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
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
[JsonProperty("phase"), Key(4)]
|
|
|
|
|
|
public float2 FixedPosition = 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
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
// [JsonProperty("period"), Key(4)]
|
|
|
|
|
|
// public float Period;
|
|
|
|
|
|
|
|
|
|
|
|
public static float2 Evaluate(float phase)
|
|
|
|
|
|
{
|
|
|
|
|
|
phase *= PI * 2;
|
|
|
|
|
|
return new float2(cos(phase), sin(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
|
|
|
|
}
|