using Sand.Core; namespace Sand.ChunkPrototype; public sealed partial class PrototypeSparseSandAdapter { private void TickCellAging(int x, int y, PrototypeParticle particle) { if (!TryGetCellPage(x, y, out _, out var page, out var localX, out var localY)) { return; } page.SetCellAge(localX, localY, page.GetCellAge(localX, localY) + 1f); if (page.GetIntegrity(localX, localY) < particle.Durability) { page.SetIntegrity(localX, localY, MathF.Min(particle.Durability, page.GetIntegrity(localX, localY) + MathF.Max(0.02f, particle.Hardness * 0.03f))); } } private bool TickLifetime(int x, int y, ref PrototypeParticle particle) { if (!TryGetCellPage(x, y, out _, out var page, out var localX, out var localY)) { return false; } var lifetime = page.GetLifetime(localX, localY); if (lifetime > 0f) { lifetime -= 1f; page.SetLifetime(localX, localY, lifetime); } if (lifetime <= 0f && particle.DefaultLifetime > 0f) { if (particle.MotionType == PrototypeParticleType.Steam && particle.SolidifyTypeId != 0 && TryResolveProfile(particle.SolidifyTypeId, out var condensed)) { var currentTemperature = GetTemperatureAt(x, y); ReplaceParticle(x, y, condensed); if (TryGetCellPage(x, y, out _, out var targetPage, out var targetLocalX, out var targetLocalY)) { targetPage.SetTemperature(targetLocalX, targetLocalY, MathF.Max(condensed.InitialTemperature, MathF.Min(currentTemperature, 95f))); } particle = condensed; return false; } RemoveParticle(x, y); return true; } return false; } private void AutoIgnite(int x, int y, PrototypeParticle particle) { if (!TryGetCellPage(x, y, out _, out var page, out var localX, out var localY)) { return; } if (page.GetBurning(localX, localY) == 0 && particle.BurnTemperature > 0f && GetTemperatureAt(x, y) >= particle.BurnTemperature && particle.Flamability > 0f) { page.SetBurning(localX, localY, 1); page.SetBurnTime(localX, localY, MathF.Max(particle.BurnDuration, 1f)); } } private void ApplyRuntimeEmissionAndProduction(int x, int y, PrototypeParticle particle, ref int seed) { var isEffectEmitter = particle.BehaviorKind is ParticleBehaviorKind.Fire or ParticleBehaviorKind.BurningWood or ParticleBehaviorKind.Ember or ParticleBehaviorKind.Plasma; var emission = particle.HeatEmission; if (particle.BehaviorKind == ParticleBehaviorKind.Fire && emission <= 0f) { emission = 24f; } else if (particle.BehaviorKind == ParticleBehaviorKind.Plasma && emission <= 0f) { emission = 80f; } else if (particle.BehaviorKind == ParticleBehaviorKind.Ember && emission <= 0f) { emission = 12f; } else if (particle.IsMolten && emission <= 0f) { emission = particle.TypeId != 0 ? 28f : 0f; } if (emission > 0f) { ApplyHeatEmission(x, y, emission); } if ((isEffectEmitter || IsBurningAt(x, y)) && particle.ProduceTypeId != 0 && NextChance(ref seed) <= 0.08f) { TrySpawnAtOffset(x, y, 0, -1, particle.ProduceTypeId); } if (particle.SmokeSpawnChance > 0f && isEffectEmitter && TryFindProfileById("smoke", out var smoke) && NextChance(ref seed) <= particle.SmokeSpawnChance) { TrySpawnAtOffset(x, y, 0, -1, smoke.TypeId); } if (particle.EmberSpawnChance > 0f && isEffectEmitter && TryFindProfileById("ember", out var ember) && NextChance(ref seed) <= particle.EmberSpawnChance) { TrySpawnAtOffset(x, y, 0, -1, ember.TypeId); } } private bool TickBurning(int x, int y, ref PrototypeParticle particle) { if (!TryGetCellPage(x, y, out _, out var page, out var localX, out var localY) || page.GetBurning(localX, localY) == 0) { return false; } page.SetTemperature(localX, localY, page.GetTemperature(localX, localY) + 1.5f + (particle.HeatEmission * 0.02f)); page.SetBurnTime(localX, localY, page.GetBurnTime(localX, localY) - MathF.Max(0.05f, particle.BurnRate)); if (page.GetBurnTime(localX, localY) > 0f) { return false; } RemoveParticle(x, y); return true; } private bool TickSpecialBehavior(int x, int y, PrototypeParticle particle, ref int seed) { var temperature = GetTemperatureAt(x, y); switch (particle.BehaviorKind) { case ParticleBehaviorKind.Fire: if (temperature < 120f && NextChance(ref seed) <= 0.1f) { RemoveParticle(x, y); return true; } break; case ParticleBehaviorKind.Ember: if (temperature < 60f && NextChance(ref seed) <= 0.1f) { RemoveParticle(x, y); return true; } break; case ParticleBehaviorKind.BurningWood: if (particle.EmberSpawnChance > 0f && TryFindProfileById("ember", out var ember) && NextChance(ref seed) <= particle.EmberSpawnChance * 0.5f) { TrySpawnAtOffset(x, y, 0, -1, ember.TypeId); } break; } return false; } private bool IsBurningAt(int x, int y) { if (!TryGetCellPage(x, y, out _, out var page, out var localX, out var localY)) { return false; } return page.GetBurning(localX, localY) != 0; } private bool TrySpawnAtOffset(int x, int y, int offsetX, int offsetY, ushort typeId) { var tx = x + offsetX; var ty = y + offsetY; if (!InBounds(tx, ty) || HasParticle(tx, ty) || !TryResolveProfile(typeId, out var particle)) { return false; } return AddParticle(tx, ty, particle); } }