Aetheria/tests/Aetheria.Shared.Tests/ZoneWorlds.cs
Meta Vens 65aa61811e Well-law tests follow the subzone law: two suns 600 apart are two subzones, not a superposition
WellsSuperpose is removed (its rule, roots superpose everywhere, is the one multi-root-zone-shape decides); HeightIsThePotentialOverOneK and FlowFieldTests probe inside each well's reach. ZoneWorlds gains Belt and AsteroidSize; fade and ring tolerances are relative to the Plummer pull. Not yet run on dotnet at this commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 21:32:30 +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, Rarity = 0, 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(cache, new ProvenanceLedger(), GameSettings(), _ => { }) };
}
}