Aetheria/tests/Aetheria.Shared.Tests/ZoneWorlds.cs
soul cc0c164574 determinism-ledger: every ItemManager states its seed; scatter on the zone's dice
- ItemManager(uint seed, ...) refuses seed 0 and seeds Random from it.
- RunStart.ItemSeed(galaxy, step) is public (formula unchanged), refuses a
  zero result; named steps ArenaStep, StageStep, ContinueStep and NoRunSeed.
- Continue seeds the item stream from the saved run's seed (ContinueStep);
  a scene with no galaxy uses NoRunSeed.
- Zone's unpack scatter draws SimulationDice.For(CombatSeed, ScatterStream,
  1-based pack ordinal), never the item stream.
- Loadout's scratch manager and AetherDb's catalog command take fixed seeds;
  every test and Editor construction gains 1u (mechanical rewrite).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 19:01:41 +02:00

85 lines
3.6 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CultMath;
using GameCult.Caching;
using Xunit;
using static CultMath.math;
using float2 = CultMath.float2;
using float3 = CultMath.float3;
// The hand-built zones of the tree tests: packs of orbits and bodies read by the production Zone constructor with the shipped
// well curves (c = 45.08 m^.2, eps = 7.32 m^.25), each in its own store under one temporary root.
internal sealed class ZoneWorlds : IDisposable
{
public readonly string Root = Path.Combine(Path.GetTempPath(), "aetheria-orbitsfield-" + Guid.NewGuid().ToString("N"));
public readonly List<CultCache> Caches = new List<CultCache>();
public ZoneWorlds() => Directory.CreateDirectory(Root);
public void Dispose()
{
foreach (var cache in Caches) cache.Dispose();
Directory.Delete(Root, true);
}
private static GameplaySettings GameSettings() => new GameplaySettings
{
DefaultEntitySettings = new EntitySettings(),
Tiers = new[] { new RarityTier { Name = "Common", Quality = .5f, Color = new float3(1, 1, 1) } },
QualityPriceModifier = new ExponentialLerp()
};
public static PlanetSettings Curves() => new PlanetSettings
{
WellStrength = new ExponentialCurve { Exponent = .2f, Multiplier = 45.08f },
RocheLimit = new ExponentialCurve { Exponent = .25f, Multiplier = 7.32f },
BodyRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 3f },
LightRadius = new ExponentialCurve(),
AsteroidSize = new ExponentialLerp { Minimum = 2f, Maximum = 9f, Exponent = 1.7f }
};
public sealed class World
{
public CultCache Cache;
public ItemManager Items;
public ZonePack Pack = new ZonePack { Radius = 20000f, Mass = 10000f };
public CultRecordKey Orbit(CultRecordKey parent, float distance, float phase = 0f)
{
var data = new OrbitData { Distance = distance, Phase = phase };
if (parent.IsSet()) data.Parent = new CultRecordRef<OrbitData>(parent);
var key = Cache.Upsert(data).Key;
Pack.Orbits.Add(new CultRecordRef<OrbitData>(key));
return key;
}
public CultRecordKey Body(CultRecordKey orbit, float mass)
{
var key = Cache.Upsert(new PlanetData { Name = "Body", Orbit = new CultRecordRef<OrbitData>(orbit), Mass = mass }).Key;
Pack.Planets.Add(new CultRecordRef<BodyData>(key));
return key;
}
// A belt of asteroids at the given distances riding `orbit`; returns the belt's body key.
public CultRecordKey Belt(CultRecordKey orbit, params float[] distances)
{
var asteroids = distances.Select((d, i) => new Asteroid { Distance = d, Phase = frac(i * .6180339887f), Size = .5f, RotationSpeed = .1f }).ToArray();
var key = Cache.Upsert(new AsteroidBeltData { Orbit = new CultRecordRef<OrbitData>(orbit), Asteroids = asteroids }).Key;
Pack.Planets.Add(new CultRecordRef<BodyData>(key));
return key;
}
public Zone Build() => new Zone(Items, Curves(), Pack, new GalaxyZone { Name = "OrbitsFromField", Owner = null }, null);
}
public World New()
{
var dir = Path.Combine(Root, $"world{Caches.Count}");
Directory.CreateDirectory(dir);
var cache = AetheriaStores.Open(Path.Combine(dir, "Aetheria.cc"), Path.Combine(dir, "run.cc"), catalogWritable: true);
Caches.Add(cache);
return new World { Cache = cache, Items = new ItemManager(1u, cache, new ProvenanceLedger(), GameSettings(), _ => { }) };
}
}