DiamondAtmosphere.Shatter.cs
using System;
namespace Diamonds;
public sealed partial class DiamondAtmosphere
{
const int MaxShatterPushes = 8;
float shatterPushCooldown;
float? shatterPushInterval;
public float ShatterPushInterval { get => shatterPushInterval ?? 0.1f; set => shatterPushInterval = Math.Clamp( value, 0, 1 ); }
void PushShatterDye( DiamondBoard board, int first )
{
int count = board.ShatterBursts.Count - first;
if ( count <= 0 || shatterPushCooldown > 0 ) return;
shatterPushCooldown = ShatterPushInterval;
int pushes = Math.Min( count, MaxShatterPushes );
// Sample across large batches without allocating or queuing delayed explosions.
// Emission still happens for every gem, even when its push is skipped.
for ( int i = 0; i < pushes; i++ )
{
int index = pushes == 1 ? first : first + i * (count - 1) / (pushes - 1);
PushShatter( board.ShatterBursts[index].Cell );
}
}
void PushShatter( DiamondBoard.Diamond cell )
{
const float radius = 126f, strength = 8f;
float cx = DiamondBoard.LaneX( cell.Lane ), cy = cell.Y;
var (gx, gy) = WorldToGrid( cx, cy );
float reach = radius / CellSize;
int left = Math.Max( 1, (int)MathF.Floor( gx - reach ) );
int right = Math.Min( Columns - 2, (int)MathF.Ceiling( gx + reach ) );
int top = Math.Max( 1, (int)MathF.Floor( gy - reach ) );
int bottom = Math.Min( Rows - 2, (int)MathF.Ceiling( gy + reach ) );
for ( int y = top; y <= bottom; y++ )
for ( int x = left; x <= right; x++ )
{
int n = y * Columns + x;
if ( wallDistance[n] < 0 ) continue;
var point = GridToWorld( x, y );
float dx = (point.x - cx) / radius, dy = (point.y - cy) / radius;
float distanceSquared = dx * dx + dy * dy;
if ( distanceSquared >= 1 ) continue;
// Smooth radial impulse, zero at the center and perimeter. Reuse the
// normal 30 Hz solver and obstacle-aware transport; no extra fluid pass.
float falloff = 1 - distanceSquared;
float force = strength * 3.5f * falloff * falloff;
float vx = velocityX[n] + dx * force, vy = velocityY[n] + dy * force;
float speedSquared = vx * vx + vy * vy;
float scale = speedSquared > MaxFlowVelocity * MaxFlowVelocity ? MaxFlowVelocity / MathF.Sqrt( speedSquared ) : 1;
velocityX[n] = vx * scale; velocityY[n] = vy * scale;
influence[n] = MathF.Max( influence[n], falloff );
}
}
}