347 lines
12 KiB
C#
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;
|
|
}
|