Aetheria/Aetheria.State.Daemon/AetheriaDaemonTutorialRunWriter.cs
2026-07-17 09:23:29 +01:00

114 lines
5.7 KiB
C#

using Aetheria.State;
using Aetheria.State.Documents;
using GameCult.Aetheria.State.Verse;
using GameCult.Caching;
public sealed record AetheriaDaemonWrittenRun(
string RunId,
string RunKey,
string CurrentEntityKey,
bool IsTutorial);
public static class AetheriaDaemonTutorialRunWriter
{
public const string RunId = "tutorial";
public const uint GenerationSeed = 0xA37E_2026u;
public static async Task<AetheriaDaemonWrittenRun> WriteAsync(
AetheriaStateNode node,
AetheriaRuntimeCatalogSnapshot catalog,
string now)
{
var factions = AetheriaDaemonTutorialTopologyGenerator.ResolveFossilFactions(catalog);
var world = AetheriaDaemonTutorialWorldGenerator.Generate(factions, GenerationSeed);
var materialized = AetheriaDaemonTutorialWorldMaterializer.Materialize(world, factions, catalog);
var runKey = RunKey();
var zoneKeys = materialized.Topology.Zones
.OrderBy(zone => zone.ZoneIndex)
.Select(zone => ZoneKey(zone.ZoneIndex))
.ToArray();
var corporationIndices = (catalog.Corporations ?? Array.Empty<AetheriaRuntimeCorporation>())
.Select((corporation, index) => (corporation.CorporationKey, index))
.Where(value => !string.IsNullOrWhiteSpace(value.CorporationKey))
.ToDictionary(value => value.CorporationKey, value => value.index, StringComparer.Ordinal);
var playerEntityKey = EntityKey(materialized.PlayerZoneIndex, materialized.PlayerEntityIndex);
foreach (var zone in materialized.Zones.Values.OrderBy(zone => zone.Topology.ZoneIndex))
{
var entityKeys = Enumerable.Range(0, zone.Entities.Length)
.Select(index => EntityKey(zone.Topology.ZoneIndex, index))
.ToArray();
await node.MutableDocument<AetheriaZoneState>(new CultRecordKey(ZoneKey(zone.Topology.ZoneIndex)))
.ReplaceAsync(new AetheriaZoneState
{
Name = zone.Topology.Name,
Position = new AetheriaVector2 { X = zone.Topology.X, Y = zone.Topology.Y },
AdjacentZoneIndices = zone.Topology.AdjacentZoneIndices.ToArray(),
FactionIndices = zone.Topology.FactionKeys
.Select(key => corporationIndices[key])
.ToArray(),
OwnerFactionIndex = string.IsNullOrWhiteSpace(zone.Topology.OwnerFactionKey)
? -1
: corporationIndices[zone.Topology.OwnerFactionKey],
EntityKeys = entityKeys,
Orbits = zone.Orbits,
Bodies = zone.CelestialPlan.Bodies,
GravityTerrainRadius = zone.CelestialPlan.Radius,
GravityTerrainDepth = zone.CelestialPlan.GravityTerrainDepth,
GravityTerrainDepthExponent = zone.CelestialPlan.GravityTerrainDepthExponent,
GravityTerrainBoundaryFog = zone.CelestialPlan.GravityTerrainBoundaryFog,
GravityTerrainWaveFrequency = 1,
DroppedPickups = [],
NextPickupIndex = 0
}).ConfigureAwait(false);
for (var entityIndex = 0; entityIndex < zone.Entities.Length; entityIndex++)
{
await node.MutableDocument<AetheriaEntitySnapshot>(new CultRecordKey(entityKeys[entityIndex]))
.ReplaceAsync(zone.Entities[entityIndex])
.ConfigureAwait(false);
}
}
await node.MutableDocument<AetheriaRunState>(new CultRecordKey(runKey)).ReplaceAsync(new AetheriaRunState
{
RunId = RunId,
IsTutorial = true,
EntranceZoneIndex = materialized.Topology.EntranceZoneIndex,
ExitZoneIndex = -1,
CurrentZoneIndex = materialized.PlayerZoneIndex,
DiscoveredZoneIndices = materialized.Topology.DiscoveredZoneIndices.ToArray(),
ZoneKeys = zoneKeys,
FactionRelationships = factions.Select(faction => new AetheriaFactionRelationshipState
{
FactionKey = faction.CorporationKey,
Relationship = "Neutral",
Standing = 0
}).ToArray(),
GenerationSeed = GenerationSeed,
CurrentEntityKey = playerEntityKey,
LifecyclePhase = AetheriaRuntimeRunLifecycle.Active,
TerminalFrameId = -1,
AgentTasks = materialized.Zones.Values
.OrderBy(zone => zone.Topology.ZoneIndex)
.SelectMany(zone => zone.AgentTasks)
.ToArray(),
UpdatedAtUtc = now
}).ConfigureAwait(false);
var settings = await node.MutableDocument<AetheriaPlayerSettings>(AetheriaStateNode.PlayerSettingsKey)
.ReadAsync().ConfigureAwait(false) ?? new AetheriaPlayerSettings();
settings.ActiveRunKey = runKey;
settings.PlayerName = string.IsNullOrWhiteSpace(settings.PlayerName) ? "Pilot" : settings.PlayerName;
settings.LastUpdatedAtUtc = now;
await node.MutableDocument<AetheriaPlayerSettings>(AetheriaStateNode.PlayerSettingsKey)
.ReplaceAsync(settings)
.ConfigureAwait(false);
await node.FlushAsync().ConfigureAwait(false);
return new AetheriaDaemonWrittenRun(RunId, runKey, playerEntityKey, true);
}
public static string RunKey() => $"global:aetheria.run_state.{RunId}.v1";
public static string ZoneKey(int zoneIndex) => $"global:aetheria.zone_state.{RunId}.{zoneIndex}.v1";
public static string EntityKey(int zoneIndex, int entityIndex) =>
$"global:aetheria.run_state.{RunId}.zone.{zoneIndex}.entity.{entityIndex}.v1";
}