sandpypi/Sand.ChunkPrototype/ChunkCellPage.cs

347 lines
12 KiB
C#

namespace Sand.ChunkPrototype;
internal sealed class ChunkCellPage
{
private readonly PrototypeParticle[] _particles;
private readonly int[] _processedStep;
private readonly int[] _occupiedRowCounts;
private readonly int[] _gasRowCounts;
private readonly int[] _rowRevision;
private readonly int[] _blockedSolidSignature;
private readonly int[] _blockedGasRiseSignature;
private readonly int[] _gasRetrySignature;
private readonly int[] _gasRetryUntilStep;
private readonly int[] _gasRowCooldownSignature;
private readonly int[] _gasRowCooldownUntilStep;
private readonly sbyte[] _driftState;
private readonly float[] _pressureDuration;
private readonly float[] _temperature;
private readonly float[] _burnTime;
private readonly byte[] _burning;
private readonly short[] _sparkTime;
private readonly float[] _lifetime;
private readonly float[] _cellAge;
private readonly float[] _integrity;
public ChunkCellPage(int width, int height)
{
Width = width;
Height = height;
_particles = new PrototypeParticle[width * height];
_processedStep = new int[width * height];
_occupiedRowCounts = new int[height];
_gasRowCounts = new int[height];
_rowRevision = new int[height];
_blockedSolidSignature = new int[width * height];
_blockedGasRiseSignature = new int[width * height];
_gasRetrySignature = new int[width * height];
_gasRetryUntilStep = new int[width * height];
_gasRowCooldownSignature = new int[height];
_gasRowCooldownUntilStep = new int[height];
_driftState = new sbyte[width * height];
_pressureDuration = new float[width * height];
_temperature = new float[width * height];
_burnTime = new float[width * height];
_burning = new byte[width * height];
_sparkTime = new short[width * height];
_lifetime = new float[width * height];
_cellAge = new float[width * height];
_integrity = new float[width * height];
ClearDirtyBands();
ClearVisualDirty();
}
public int Width { get; }
public int Height { get; }
public int OccupancyCount { get; private set; }
public int GasCellCount { get; private set; }
public int DynamicCellCount { get; private set; }
public int RuntimeCellCount { get; private set; }
public int PendingActiveSteps { get; private set; }
public bool IsActive { get; set; } = true;
public bool HasFieldActivity { get; set; }
public int LastTouchedFrame { get; set; }
public int DirtyMinRow { get; private set; }
public int DirtyMaxRow { get; private set; }
public int DirtyMinCol { get; private set; }
public int DirtyMaxCol { get; private set; }
public bool HasDirtyBands { get; private set; }
public int VisualDirtyMinRow { get; private set; }
public int VisualDirtyMaxRow { get; private set; }
public int VisualDirtyMinCol { get; private set; }
public int VisualDirtyMaxCol { get; private set; }
public bool HasVisualDirty { get; private set; }
public PrototypeParticle this[int localX, int localY]
{
get => _particles[GetIndex(localX, localY)];
set => _particles[GetIndex(localX, localY)] = value;
}
public bool IsProcessed(int localX, int localY, int stepToken) => _processedStep[GetIndex(localX, localY)] == stepToken;
public void MarkProcessed(int localX, int localY, int stepToken) => _processedStep[GetIndex(localX, localY)] = stepToken;
public bool IsOccupied(int localX, int localY) => _particles[GetIndex(localX, localY)].TypeId != 0;
public int GetOccupiedRowCount(int localY) => _occupiedRowCounts[localY];
public int GetGasRowCount(int localY) => _gasRowCounts[localY];
public int GetRowRevision(int localY) => _rowRevision[localY];
public int GetBlockedSolidSignature(int localX, int localY) => _blockedSolidSignature[GetIndex(localX, localY)];
public void SetBlockedSolidSignature(int localX, int localY, int signature) => _blockedSolidSignature[GetIndex(localX, localY)] = signature;
public int GetBlockedGasRiseSignature(int localX, int localY) => _blockedGasRiseSignature[GetIndex(localX, localY)];
public void SetBlockedGasRiseSignature(int localX, int localY, int signature) => _blockedGasRiseSignature[GetIndex(localX, localY)] = signature;
public int GetGasRetrySignature(int localX, int localY) => _gasRetrySignature[GetIndex(localX, localY)];
public int GetGasRetryUntilStep(int localX, int localY) => _gasRetryUntilStep[GetIndex(localX, localY)];
public void SetGasRetryState(int localX, int localY, int signature, int untilStep)
{
var index = GetIndex(localX, localY);
_gasRetrySignature[index] = signature;
_gasRetryUntilStep[index] = untilStep;
}
public void ClearGasRetryState(int localX, int localY)
{
var index = GetIndex(localX, localY);
_gasRetrySignature[index] = 0;
_gasRetryUntilStep[index] = 0;
}
public int GetGasRowCooldownSignature(int localY) => _gasRowCooldownSignature[localY];
public int GetGasRowCooldownUntilStep(int localY) => _gasRowCooldownUntilStep[localY];
public void SetGasRowCooldown(int localY, int signature, int untilStep)
{
_gasRowCooldownSignature[localY] = signature;
_gasRowCooldownUntilStep[localY] = untilStep;
}
public void ClearGasRowCooldown(int localY)
{
_gasRowCooldownSignature[localY] = 0;
_gasRowCooldownUntilStep[localY] = 0;
}
public sbyte GetDriftState(int localX, int localY) => _driftState[GetIndex(localX, localY)];
public void SetDriftState(int localX, int localY, sbyte driftState) => _driftState[GetIndex(localX, localY)] = driftState;
public float GetPressureDuration(int localX, int localY) => _pressureDuration[GetIndex(localX, localY)];
public void SetPressureDuration(int localX, int localY, float value) => _pressureDuration[GetIndex(localX, localY)] = value;
public float GetTemperature(int localX, int localY) => _temperature[GetIndex(localX, localY)];
public void SetTemperature(int localX, int localY, float value) => _temperature[GetIndex(localX, localY)] = value;
public float GetBurnTime(int localX, int localY) => _burnTime[GetIndex(localX, localY)];
public void SetBurnTime(int localX, int localY, float value) => _burnTime[GetIndex(localX, localY)] = value;
public byte GetBurning(int localX, int localY) => _burning[GetIndex(localX, localY)];
public void SetBurning(int localX, int localY, byte value) => _burning[GetIndex(localX, localY)] = value;
public short GetSparkTime(int localX, int localY) => _sparkTime[GetIndex(localX, localY)];
public void SetSparkTime(int localX, int localY, short value) => _sparkTime[GetIndex(localX, localY)] = value;
public float GetLifetime(int localX, int localY) => _lifetime[GetIndex(localX, localY)];
public void SetLifetime(int localX, int localY, float value) => _lifetime[GetIndex(localX, localY)] = value;
public float GetCellAge(int localX, int localY) => _cellAge[GetIndex(localX, localY)];
public void SetCellAge(int localX, int localY, float value) => _cellAge[GetIndex(localX, localY)] = value;
public float GetIntegrity(int localX, int localY) => _integrity[GetIndex(localX, localY)];
public void SetIntegrity(int localX, int localY, float value) => _integrity[GetIndex(localX, localY)] = value;
public void SetCell(int localX, int localY, PrototypeParticle particle)
{
var index = GetIndex(localX, localY);
var previous = _particles[index];
if (previous.TypeId == 0 && particle.TypeId != 0)
{
OccupancyCount++;
_occupiedRowCounts[localY]++;
_rowRevision[localY]++;
}
else if (previous.TypeId != 0 && particle.TypeId == 0)
{
OccupancyCount--;
_occupiedRowCounts[localY]--;
_rowRevision[localY]++;
}
else if (previous.TypeId != particle.TypeId)
{
_rowRevision[localY]++;
}
if (previous.MotionType == PrototypeParticleType.Steam)
{
GasCellCount--;
_gasRowCounts[localY]--;
}
if (previous.TypeId != 0 && !previous.IsStatic)
{
DynamicCellCount--;
}
if (previous.TypeId != 0 && previous.RequiresFullRuntimeStep)
{
RuntimeCellCount--;
}
_particles[index] = particle;
if (particle.MotionType == PrototypeParticleType.Steam)
{
GasCellCount++;
_gasRowCounts[localY]++;
}
if (particle.TypeId != 0 && !particle.IsStatic)
{
DynamicCellCount++;
}
if (particle.TypeId != 0 && particle.RequiresFullRuntimeStep)
{
RuntimeCellCount++;
}
if (particle.TypeId == 0)
{
_driftState[index] = 0;
_pressureDuration[index] = 0f;
_temperature[index] = 0f;
_burnTime[index] = 0f;
_burning[index] = 0;
_sparkTime[index] = 0;
_lifetime[index] = 0f;
_cellAge[index] = 0f;
_integrity[index] = 0f;
}
_blockedSolidSignature[index] = 0;
_blockedGasRiseSignature[index] = 0;
_gasRetrySignature[index] = 0;
_gasRetryUntilStep[index] = 0;
_gasRowCooldownSignature[localY] = 0;
_gasRowCooldownUntilStep[localY] = 0;
MarkTouched(localX, localY);
}
public void MarkTouched(int localX, int localY)
{
MarkDirtyBand(localX, localY);
MarkVisualDirty(localX, localY);
IsActive = true;
PendingActiveSteps = Math.Max(PendingActiveSteps, 1);
}
public void RequestFutureSteps(int steps)
{
if (steps > PendingActiveSteps)
{
PendingActiveSteps = steps;
}
}
public void DecayPendingSteps()
{
if (PendingActiveSteps > 0)
{
PendingActiveSteps--;
}
}
public void MarkDirtyBand(int localX, int localY)
{
if (!HasDirtyBands)
{
DirtyMinRow = DirtyMaxRow = localY;
DirtyMinCol = DirtyMaxCol = localX;
HasDirtyBands = true;
return;
}
DirtyMinRow = Math.Min(DirtyMinRow, localY);
DirtyMaxRow = Math.Max(DirtyMaxRow, localY);
DirtyMinCol = Math.Min(DirtyMinCol, localX);
DirtyMaxCol = Math.Max(DirtyMaxCol, localX);
}
public void MarkVisualDirty(int localX, int localY)
{
if (!HasVisualDirty)
{
VisualDirtyMinRow = VisualDirtyMaxRow = localY;
VisualDirtyMinCol = VisualDirtyMaxCol = localX;
HasVisualDirty = true;
return;
}
VisualDirtyMinRow = Math.Min(VisualDirtyMinRow, localY);
VisualDirtyMaxRow = Math.Max(VisualDirtyMaxRow, localY);
VisualDirtyMinCol = Math.Min(VisualDirtyMinCol, localX);
VisualDirtyMaxCol = Math.Max(VisualDirtyMaxCol, localX);
}
public void MarkFullVisualDirty()
{
HasVisualDirty = true;
VisualDirtyMinRow = 0;
VisualDirtyMaxRow = Height - 1;
VisualDirtyMinCol = 0;
VisualDirtyMaxCol = Width - 1;
}
public void ClearDirtyBands()
{
HasDirtyBands = false;
DirtyMinRow = Height;
DirtyMaxRow = -1;
DirtyMinCol = Width;
DirtyMaxCol = -1;
}
public void ClearVisualDirty()
{
HasVisualDirty = false;
VisualDirtyMinRow = Height;
VisualDirtyMaxRow = -1;
VisualDirtyMinCol = Width;
VisualDirtyMaxCol = -1;
}
public IEnumerable<(int LocalX, int LocalY, PrototypeParticle Particle)> EnumerateOccupied()
{
for (var y = 0; y < Height; y++)
{
for (var x = 0; x < Width; x++)
{
var particle = _particles[GetIndex(x, y)];
if (particle.TypeId != 0)
{
yield return (x, y, particle);
}
}
}
}
private int GetIndex(int localX, int localY) => (localY * Width) + localX;
}