sandpypi/Sand.ChunkPrototype.Tests/PrototypeSparseSandAdapterTests.cs

526 lines
23 KiB
C#

using FluentAssertions;
using Sand.ChunkPrototype;
using Sand.Core;
namespace Sand.ChunkPrototype.Tests;
public sealed class PrototypeSparseSandAdapterTests
{
[Fact]
public void StepDownMovesParticlesIntoEmptySpace()
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(3, 2);
var moves = adapter.StepDown();
moves.Should().Be(1);
adapter.HasParticle(3, 2).Should().BeFalse();
adapter.HasParticle(3, 3).Should().BeTrue();
adapter.World.IsOccupied(3, 3).Should().BeTrue();
adapter.LastStepStats.MovementOnlyFastPathCount.Should().Be(1);
adapter.LastStepStats.FullRuntimeStepCount.Should().Be(0);
adapter.LastStepStats.MoveAttempts.Should().BeGreaterThan(0);
adapter.LastStepStats.SuccessfulMoves.Should().Be(1);
}
[Fact]
public void StepDownStopsAtBottomBoundary()
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(3, 15);
var moves = adapter.StepDown();
moves.Should().Be(0);
adapter.HasParticle(3, 15).Should().BeTrue();
}
[Fact]
public void WaterFlowsSidewaysWhenBlockedBelow()
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(8, 8, PrototypeParticleType.Water);
adapter.AddParticle(8, 9, PrototypeParticleType.Wall);
adapter.AddParticle(7, 9, PrototypeParticleType.Wall);
adapter.AddParticle(9, 9, PrototypeParticleType.Wall);
var moves = adapter.Step();
moves.Should().Be(1);
(adapter.GetParticleTypeAt(7, 8) == PrototypeParticleType.Water || adapter.GetParticleTypeAt(9, 8) == PrototypeParticleType.Water).Should().BeTrue();
}
[Fact]
public void SteamRisesWhenSpaceAboveIsEmpty()
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(8, 8, PrototypeParticleType.Steam);
var moves = adapter.Step();
moves.Should().Be(1);
adapter.GetParticleTypeAt(8, 7).Should().Be(PrototypeParticleType.Steam);
}
[Fact]
public void SteamCrossingHorizontalChunkBoundaryDoesNotPauseBehindUpperChunkGas()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(2, 3, PrototypeParticleType.Steam);
adapter.AddParticle(2, 4, PrototypeParticleType.Steam);
adapter.AddParticle(1, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 3, PrototypeParticleType.Wall);
adapter.AddParticle(1, 4, PrototypeParticleType.Wall);
adapter.AddParticle(3, 4, PrototypeParticleType.Wall);
var moves = adapter.Step();
moves.Should().Be(2);
adapter.GetParticleTypeAt(2, 2).Should().Be(PrototypeParticleType.Steam);
adapter.GetParticleTypeAt(2, 3).Should().Be(PrototypeParticleType.Steam);
adapter.GetParticleTypeAt(2, 4).Should().Be(PrototypeParticleType.Empty);
}
[Fact]
public void DenseSteamColumnAcrossHorizontalChunkBoundaryDoesNotLeaveGapRow()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(2, 3, PrototypeParticleType.Steam);
adapter.AddParticle(2, 4, PrototypeParticleType.Steam);
adapter.AddParticle(2, 5, PrototypeParticleType.Steam);
adapter.AddParticle(1, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 3, PrototypeParticleType.Wall);
adapter.AddParticle(1, 4, PrototypeParticleType.Wall);
adapter.AddParticle(3, 4, PrototypeParticleType.Wall);
adapter.AddParticle(1, 5, PrototypeParticleType.Wall);
adapter.AddParticle(3, 5, PrototypeParticleType.Wall);
var moves = adapter.Step();
moves.Should().Be(3);
adapter.GetParticleTypeAt(2, 2).Should().Be(PrototypeParticleType.Steam);
adapter.GetParticleTypeAt(2, 3).Should().Be(PrototypeParticleType.Steam);
adapter.GetParticleTypeAt(2, 4).Should().Be(PrototypeParticleType.Steam);
}
[Fact]
public void GasSeamFrameClearsOldBorderPixelAfterColumnCompaction()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(2, 3, PrototypeParticleType.Steam);
adapter.AddParticle(2, 4, PrototypeParticleType.Steam);
adapter.AddParticle(2, 5, PrototypeParticleType.Steam);
adapter.AddParticle(1, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 3, PrototypeParticleType.Wall);
adapter.AddParticle(1, 4, PrototypeParticleType.Wall);
adapter.AddParticle(3, 4, PrototypeParticleType.Wall);
adapter.AddParticle(1, 5, PrototypeParticleType.Wall);
adapter.AddParticle(3, 5, PrototypeParticleType.Wall);
adapter.Step();
var frame = adapter.BuildRgbaFrame().ToArray();
var clearedIndex = ((5 * 8) + 2) * 4;
frame[clearedIndex].Should().Be(0);
frame[clearedIndex + 1].Should().Be(0);
frame[clearedIndex + 2].Should().Be(0);
}
[Fact]
public void GasRenderFillsChunkRowSeamWhenCloudExistsAboveAndBelow()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(2, 2, PrototypeParticleType.Steam);
adapter.AddParticle(2, 4, PrototypeParticleType.Steam);
adapter.AddParticle(1, 3, PrototypeParticleType.Steam);
adapter.AddParticle(3, 3, PrototypeParticleType.Steam);
var frame = adapter.BuildRgbaFrame().ToArray();
var seamIndex = ((3 * 8) + 2) * 4;
frame[seamIndex].Should().Be(182);
frame[seamIndex + 1].Should().Be(196);
frame[seamIndex + 2].Should().Be(214);
}
[Fact]
public void WallRemainsStaticDuringStep()
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(8, 8, PrototypeParticleType.Wall);
var moves = adapter.Step();
moves.Should().Be(0);
adapter.GetParticleTypeAt(8, 8).Should().Be(PrototypeParticleType.Wall);
}
[Fact]
public void StepDownCrossesChunkBoundaryAndKeepsCountsConsistent()
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(3, 3);
adapter.World.ClearDirtyChunks();
var moves = adapter.StepDown();
moves.Should().Be(1);
adapter.HasParticle(3, 4).Should().BeTrue();
adapter.World.GetChunkOccupancyCount(0, 0).Should().Be(0);
adapter.World.GetChunkOccupancyCount(0, 1).Should().Be(1);
adapter.World.DirtyChunks.Should().BeEquivalentTo([(0, 0), (0, 1)]);
adapter.LastStepStats.SuccessfulMoves.Should().Be(1);
adapter.LastStepStats.SwapAttempts.Should().Be(0);
adapter.LastStepStats.MoveAttempts.Should().BeGreaterThan(0);
}
[Fact]
public void TrimResidencyUnloadsDistantEmptyChunksAfterMovement()
{
var adapter = new PrototypeSparseSandAdapter(64, 64, new ChunkResidencyConfig(4, 4, Capacity: 64));
adapter.AddParticle(3, 3);
adapter.AddParticle(40, 3);
adapter.RemoveParticle(40, 3);
var unloaded = adapter.TrimResidency(marginChunks: 0);
unloaded.Should().BeGreaterThanOrEqualTo(1);
adapter.World.IsChunkLoaded(10, 0).Should().BeFalse();
adapter.World.IsChunkLoaded(0, 0).Should().BeTrue();
}
[Fact]
public void BuildRgbaFrameIncludesDistinctColorsForParticleTypes()
{
var adapter = new PrototypeSparseSandAdapter(8, 8);
adapter.AddParticle(1, 1, PrototypeParticleType.Sand);
adapter.AddParticle(2, 1, PrototypeParticleType.Water);
adapter.AddParticle(3, 1, PrototypeParticleType.Steam);
adapter.AddParticle(4, 1, PrototypeParticleType.Wall);
var frame = adapter.BuildRgbaFrame().ToArray();
frame[((1 * 8) + 1) * 4].Should().Be(214);
frame[((1 * 8) + 2) * 4].Should().Be(72);
frame[((1 * 8) + 3) * 4].Should().Be(182);
frame[((1 * 8) + 4) * 4].Should().Be(96);
}
[Fact]
public void CustomParticleProfilePreservesColorAndTypeId()
{
var adapter = new PrototypeSparseSandAdapter(8, 8);
var particle = new PrototypeParticle(99, "custom_water", PrototypeParticleType.Water, ParticleKind.Liquid, ParticleBehaviorKind.None, 12, 34, 56, 1.2f, 0.4f, 0.1f, 0.2f);
adapter.AddParticle(2, 3, particle).Should().BeTrue();
adapter.GetTypeIdAt(2, 3).Should().Be(99);
var frame = adapter.BuildRgbaFrame().ToArray();
var index = ((3 * 8) + 2) * 4;
frame[index].Should().Be(12);
frame[index + 1].Should().Be(34);
frame[index + 2].Should().Be(56);
}
[Fact]
public void WindBrushPushesGasSidewaysWhenUpwardPathIsBlocked()
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(8, 8, PrototypeParticleType.Steam);
adapter.AddParticle(8, 7, PrototypeParticleType.Wall);
adapter.AddParticle(7, 7, PrototypeParticleType.Wall);
adapter.AddParticle(9, 7, PrototypeParticleType.Wall);
adapter.ApplyWindBrush(8, 8, 3, 1f, 0f);
adapter.ApplyWindBrush(8, 8, 3, 1f, 0f);
var moves = adapter.Step();
moves.Should().Be(1);
adapter.GetParticleTypeAt(9, 8).Should().Be(PrototypeParticleType.Steam);
}
[Fact]
public void SolidContinuesSettlingAcrossIdleStepsWithoutExternalWorldChanges()
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(8, 6, PrototypeParticleType.Sand);
adapter.AddParticle(8, 7, PrototypeParticleType.Wall);
var moved = false;
for (var i = 0; i < 64; i++)
{
if (adapter.Step() > 0)
{
moved = true;
break;
}
}
moved.Should().BeTrue();
adapter.HasParticle(8, 6).Should().BeFalse();
}
[Fact]
public void WaterKeepsSurfaceDirectionInsteadOfImmediateBacktracking()
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(8, 8, PrototypeParticleType.Water);
adapter.AddParticle(7, 9, PrototypeParticleType.Wall);
adapter.AddParticle(8, 9, PrototypeParticleType.Wall);
adapter.AddParticle(9, 9, PrototypeParticleType.Wall);
adapter.Step().Should().Be(1);
for (var i = 0; i < 4; i++)
{
adapter.Step();
}
adapter.GetParticleTypeAt(8, 8).Should().Be(PrototypeParticleType.Empty);
}
[Fact]
public void WaterHydratesSandIntoWetSand()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
var wetSand = new PrototypeParticle(101, "wet_sand", PrototypeParticleType.Sand, ParticleKind.Solid, ParticleBehaviorKind.None, 120, 96, 64, 1.6f, 0.45f, 0.24f, 0.18f);
var sand = new PrototypeParticle(100, "sand", PrototypeParticleType.Sand, ParticleKind.Solid, ParticleBehaviorKind.None, 214, 188, 96, 1.4f, 0.65f, 0.18f, 0.08f, HydrateTargetTypeId: 101);
var water = new PrototypeParticle(102, "water", PrototypeParticleType.Water, ParticleKind.Liquid, ParticleBehaviorKind.None, 72, 132, 232, 1.0f, 0.55f, 0.04f, 0.12f, Flags: PrototypeParticleFlags.WaterLike);
adapter.RegisterParticleProfile(wetSand);
adapter.AddParticle(2, 2, sand);
adapter.AddParticle(1, 2, water);
adapter.AddParticle(1, 3, PrototypeParticleType.Wall);
adapter.AddParticle(2, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 3, PrototypeParticleType.Wall);
adapter.AddParticle(0, 2, PrototypeParticleType.Wall);
adapter.AddParticle(0, 3, PrototypeParticleType.Wall);
adapter.Step();
adapter.GetTypeIdAt(2, 2).Should().Be(101);
adapter.GetTypeIdAt(1, 2).Should().Be(0);
}
[Fact]
public void FullRuntimeParticleIncrementsFullRuntimeCounter()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
var runtimeParticle = new PrototypeParticle(150, "runtime_sand", PrototypeParticleType.Sand, ParticleKind.Solid, ParticleBehaviorKind.None, 180, 150, 90, 1.2f, 0.4f, 0.15f, 0.05f, DefaultLifetime: 20f);
adapter.RegisterParticleProfile(runtimeParticle);
adapter.AddParticle(3, 2, runtimeParticle);
adapter.Step();
adapter.LastStepStats.FullRuntimeStepCount.Should().Be(1);
adapter.LastStepStats.MovementOnlyFastPathCount.Should().Be(0);
}
[Fact]
public void StalledMovableCounterIncrementsWhenOpenPathExistsButSolidDoesNotSlip()
{
var foundStall = false;
for (var x = 2; x < 14 && !foundStall; x++)
{
var adapter = new PrototypeSparseSandAdapter(16, 16, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(x, 6, PrototypeParticleType.Sand);
adapter.AddParticle(x, 7, PrototypeParticleType.Wall);
adapter.Step();
if (adapter.LastStepStats.StalledMovableCells > 0)
{
foundStall = true;
}
}
foundStall.Should().BeTrue();
}
[Fact]
public void LavaAndWaterReactIntoStoneAndSteam()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
var stone = new PrototypeParticle(201, "stone", PrototypeParticleType.Sand, ParticleKind.Solid, ParticleBehaviorKind.None, 96, 96, 96, 1.8f, 0.25f, 0.5f, 0.4f, IsStatic: true);
var steam = new PrototypeParticle(202, "steam", PrototypeParticleType.Steam, ParticleKind.Gas, ParticleBehaviorKind.None, 182, 196, 214, 0.2f, 0.7f, 0.01f, 0.03f, SolidifyTypeId: 203, PressureThreshold: 1.2f, InitialTemperature: 110f);
var water = new PrototypeParticle(203, "water", PrototypeParticleType.Water, ParticleKind.Liquid, ParticleBehaviorKind.None, 72, 132, 232, 1.0f, 0.55f, 0.04f, 0.12f, Flags: PrototypeParticleFlags.WaterLike, EvaporateTypeId: 202);
var lava = new PrototypeParticle(204, "lava", PrototypeParticleType.Water, ParticleKind.Liquid, ParticleBehaviorKind.None, 255, 96, 24, 2f, 0.35f, 0.18f, 0.45f, Flags: PrototypeParticleFlags.HotSource, IsMolten: true, SolidifyTypeId: 201);
adapter.RegisterParticleProfile(stone);
adapter.RegisterParticleProfile(steam);
adapter.AddParticle(2, 2, lava);
adapter.AddParticle(3, 2, water);
adapter.AddParticle(1, 2, PrototypeParticleType.Wall);
adapter.AddParticle(4, 2, PrototypeParticleType.Wall);
adapter.AddParticle(2, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 3, PrototypeParticleType.Wall);
adapter.AddParticle(4, 3, PrototypeParticleType.Wall);
adapter.Step();
adapter.GetTypeIdAt(2, 2).Should().Be(201);
adapter.GetTypeIdAt(3, 2).Should().Be(202);
}
[Fact]
public void HotNeighborEvaporatesWaterIntoSteam()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
var steam = new PrototypeParticle(301, "steam", PrototypeParticleType.Steam, ParticleKind.Gas, ParticleBehaviorKind.None, 182, 196, 214, 0.2f, 0.7f, 0.01f, 0.03f, InitialTemperature: 110f);
var water = new PrototypeParticle(302, "water", PrototypeParticleType.Water, ParticleKind.Liquid, ParticleBehaviorKind.None, 72, 132, 232, 1.0f, 0.55f, 0.04f, 0.12f, Flags: PrototypeParticleFlags.WaterLike, EvaporateTypeId: 301);
var plasma = new PrototypeParticle(303, "plasma", PrototypeParticleType.Wall, ParticleKind.Solid, ParticleBehaviorKind.Plasma, 255, 64, 180, 10f, 0f, 1f, 1f, IsStatic: true, Flags: PrototypeParticleFlags.FireLike | PrototypeParticleFlags.HotSource);
adapter.RegisterParticleProfile(steam);
adapter.AddParticle(2, 2, water);
adapter.AddParticle(3, 2, plasma);
adapter.AddParticle(1, 2, PrototypeParticleType.Wall);
adapter.AddParticle(2, 3, PrototypeParticleType.Wall);
adapter.AddParticle(1, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 3, PrototypeParticleType.Wall);
adapter.Step();
(adapter.GetTypeIdAt(2, 2) == 301 || adapter.GetTypeIdAt(2, 1) == 301).Should().BeTrue();
}
[Fact]
public void PressureCanBreakStaticParticleIntoConfiguredTarget()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
var rubble = new PrototypeParticle(401, "rubble", PrototypeParticleType.Sand, ParticleKind.Solid, ParticleBehaviorKind.None, 160, 140, 120, 1.2f, 0.4f, 0.22f, 0.2f);
var brittle = new PrototypeParticle(402, "brittle", PrototypeParticleType.Wall, ParticleKind.Solid, ParticleBehaviorKind.None, 210, 210, 210, 5f, 0f, 1f, 1f, IsStatic: true, BrokenTypeId: 401, PressureThreshold: 0.35f, PressureThresholdDuration: 1);
adapter.RegisterParticleProfile(rubble);
adapter.AddParticle(4, 4, brittle);
adapter.ApplyAirBrush(4, 4, 2, 0f, 12f);
adapter.ApplyAirBrush(4, 4, 2, 0f, 12f);
adapter.Step();
adapter.GetTypeIdAt(4, 4).Should().Be(401);
}
[Fact]
public void PressurizedSteamCanCondenseIntoWater()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
var water = new PrototypeParticle(501, "water", PrototypeParticleType.Water, ParticleKind.Liquid, ParticleBehaviorKind.None, 72, 132, 232, 1.0f, 0.55f, 0.04f, 0.12f, Flags: PrototypeParticleFlags.WaterLike);
var steam = new PrototypeParticle(502, "steam", PrototypeParticleType.Steam, ParticleKind.Gas, ParticleBehaviorKind.None, 182, 196, 214, 0.2f, 0.7f, 0.01f, 0.03f, SolidifyTypeId: 501, PressureThreshold: 0.6f, InitialTemperature: 110f);
adapter.RegisterParticleProfile(water);
adapter.AddParticle(4, 4, steam);
adapter.AddParticle(4, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 3, PrototypeParticleType.Wall);
adapter.AddParticle(5, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 4, PrototypeParticleType.Wall);
adapter.AddParticle(5, 4, PrototypeParticleType.Wall);
adapter.AddParticle(4, 5, PrototypeParticleType.Wall);
adapter.ApplyAirBrush(4, 4, 1, 0f, 4f);
adapter.Step();
adapter.GetTypeIdAt(4, 4).Should().Be(501);
}
[Fact]
public void BlockedGasRiseDoesNotKeepRetryingVerticalProbeWhenCeilingIsUnchanged()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(4, 4, PrototypeParticleType.Steam);
adapter.AddParticle(4, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 3, PrototypeParticleType.Wall);
adapter.AddParticle(5, 3, PrototypeParticleType.Wall);
adapter.AddParticle(3, 4, PrototypeParticleType.Wall);
adapter.AddParticle(5, 4, PrototypeParticleType.Wall);
adapter.Step();
adapter.Step();
adapter.LastStepStats.VerticalMoveAttempts.Should().Be(0);
}
[Fact]
public void StableHotGasCanSkipFullRuntimeOnSomeSteps()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
var hotGas = new PrototypeParticle(550, "hot_gas", PrototypeParticleType.Steam, ParticleKind.Gas, ParticleBehaviorKind.None, 220, 180, 220, 0.25f, 0.45f, 0.02f, 0.03f, InitialTemperature: 1000f, Conductivity: 1f);
adapter.RegisterParticleProfile(hotGas);
adapter.AddParticle(2, 5, hotGas);
adapter.AddParticle(3, 5, hotGas);
adapter.AddParticle(2, 6, hotGas);
adapter.AddParticle(3, 6, hotGas);
var observedReducedRuntime = false;
for (var i = 0; i < 4; i++)
{
adapter.Step();
if (adapter.LastStepStats.FullRuntimeGasCount < 4)
{
observedReducedRuntime = true;
break;
}
}
observedReducedRuntime.Should().BeTrue();
}
[Fact]
public void StableHotGasAdjacentToWallCanStillSkipFullRuntime()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
var hotGas = new PrototypeParticle(551, "hot_wall_gas", PrototypeParticleType.Steam, ParticleKind.Gas, ParticleBehaviorKind.None, 220, 180, 220, 0.25f, 0.45f, 0.02f, 0.03f, InitialTemperature: 1000f, Conductivity: 1f);
adapter.RegisterParticleProfile(hotGas);
adapter.AddParticle(3, 5, hotGas);
adapter.AddParticle(4, 5, hotGas);
adapter.AddParticle(2, 5, PrototypeParticleType.Wall);
adapter.AddParticle(5, 5, PrototypeParticleType.Wall);
var observedReducedRuntime = false;
for (var i = 0; i < 4; i++)
{
adapter.Step();
if (adapter.LastStepStats.FullRuntimeGasCount < 2)
{
observedReducedRuntime = true;
break;
}
}
observedReducedRuntime.Should().BeTrue();
}
[Fact]
public void AmbientSolidDoesNotInstantlyMeltJustBecauseItHasMeltTarget()
{
var adapter = new PrototypeSparseSandAdapter(8, 8, new ChunkResidencyConfig(4, 4, Capacity: 16));
var molten = new PrototypeParticle(601, "molten_test", PrototypeParticleType.Water, ParticleKind.Liquid, ParticleBehaviorKind.None, 255, 96, 24, 2f, 0.35f, 0.18f, 0.45f, IsMolten: true, InitialTemperature: 140f);
var solid = new PrototypeParticle(602, "solid_test", PrototypeParticleType.Sand, ParticleKind.Solid, ParticleBehaviorKind.None, 140, 140, 140, 1.8f, 0.25f, 0.45f, 0.35f, MeltTypeId: 601, MeltTemperature: 900f, InitialTemperature: 22f);
adapter.RegisterParticleProfile(molten);
adapter.AddParticle(4, 4, solid);
adapter.AddParticle(4, 5, PrototypeParticleType.Wall);
adapter.Step();
adapter.GetTypeIdAt(4, 4).Should().Be(602);
}
[Fact]
public void SteamContinuesRisingAcrossIdleStepsWithoutExternalWake()
{
var adapter = new PrototypeSparseSandAdapter(8, 12, new ChunkResidencyConfig(4, 4, Capacity: 16));
adapter.AddParticle(4, 9, PrototypeParticleType.Steam);
for (var i = 0; i < 4; i++)
{
adapter.Step();
}
adapter.ParticleEntries.Should().Contain(entry =>
entry.Value.MotionType == PrototypeParticleType.Steam &&
entry.Key.Y <= 6);
}
}