132 lines
4.8 KiB
C#
132 lines
4.8 KiB
C#
using System;
|
|
|
|
namespace Mr.BrickAdventures.Tools.CaLevelGenerator;
|
|
|
|
public static class CaGenerator
|
|
{
|
|
public static bool[,] Generate(CaGeneratorSettings s)
|
|
{
|
|
var rng = new Random(s.Seed);
|
|
return s.Mode switch
|
|
{
|
|
CaMode.Cave => GenerateCave(s, rng),
|
|
CaMode.Platform => GeneratePlatform(s, rng),
|
|
CaMode.Terrain => GenerateTerrain(s, rng),
|
|
_ => GenerateCave(s, rng),
|
|
};
|
|
}
|
|
|
|
public static bool[,] Smooth(bool[,] grid) => SmoothPass(grid, 5);
|
|
|
|
// ── Cave ──────────────────────────────────────────────────────────────
|
|
private static bool[,] GenerateCave(CaGeneratorSettings s, Random rng)
|
|
{
|
|
var grid = RandomFill(s.Width, s.Height, s.FillDensity, rng);
|
|
if (s.BorderWalls) EnforceBorder(grid);
|
|
for (int i = 0; i < s.SmoothingPasses; i++)
|
|
{
|
|
grid = SmoothPass(grid, 5);
|
|
if (s.BorderWalls) EnforceBorder(grid);
|
|
}
|
|
return grid;
|
|
}
|
|
|
|
// ── Platform ──────────────────────────────────────────────────────────
|
|
private static bool[,] GeneratePlatform(CaGeneratorSettings s, Random rng)
|
|
{
|
|
int w = s.Width, h = s.Height;
|
|
var grid = new bool[w, h];
|
|
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
grid[x, h - 1] = true;
|
|
grid[x, h - 2] = true;
|
|
|
|
for (int y = 0; y < h - 2; y++)
|
|
{
|
|
// Density linearly increases from 10% at top to 60% at bottom,
|
|
// scaled by user's FillDensity relative to default 0.48.
|
|
float t = (float)y / (h - 3);
|
|
float rowDensity = (0.1f + t * 0.5f) * (s.FillDensity / 0.48f);
|
|
grid[x, y] = rng.NextDouble() < rowDensity;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < s.SmoothingPasses; i++)
|
|
{
|
|
// Threshold 4 (not 5): horizontal platform groups survive, isolated
|
|
// vertical pixels collapse into flat platforms.
|
|
grid = SmoothPass(grid, 4);
|
|
for (int x = 0; x < w; x++) { grid[x, h - 1] = true; grid[x, h - 2] = true; }
|
|
}
|
|
|
|
return grid;
|
|
}
|
|
|
|
// ── Terrain ───────────────────────────────────────────────────────────
|
|
private static bool[,] GenerateTerrain(CaGeneratorSettings s, Random rng)
|
|
{
|
|
int w = s.Width, h = s.Height;
|
|
var grid = new bool[w, h];
|
|
|
|
// Start terrain surface at ~60% from bottom (40% from top)
|
|
int surfaceRow = (int)(h * 0.4f);
|
|
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
int delta = rng.Next(-2, 3);
|
|
surfaceRow = Math.Clamp(surfaceRow + delta, h / 6, h - 2);
|
|
|
|
for (int y = surfaceRow; y < h; y++)
|
|
grid[x, y] = true;
|
|
}
|
|
|
|
for (int i = 0; i < s.SmoothingPasses; i++)
|
|
grid = SmoothPass(grid, 5);
|
|
|
|
return grid;
|
|
}
|
|
|
|
// ── Shared helpers ────────────────────────────────────────────────
|
|
private static bool[,] RandomFill(int w, int h, float density, Random rng)
|
|
{
|
|
var grid = new bool[w, h];
|
|
for (int x = 0; x < w; x++)
|
|
for (int y = 0; y < h; y++)
|
|
grid[x, y] = rng.NextDouble() < density;
|
|
return grid;
|
|
}
|
|
|
|
private static bool[,] SmoothPass(bool[,] grid, int threshold)
|
|
{
|
|
int w = grid.GetLength(0), h = grid.GetLength(1);
|
|
var next = new bool[w, h];
|
|
for (int x = 0; x < w; x++)
|
|
for (int y = 0; y < h; y++)
|
|
next[x, y] = CountSolidNeighbors(grid, x, y) >= threshold;
|
|
return next;
|
|
}
|
|
|
|
private static int CountSolidNeighbors(bool[,] grid, int cx, int cy)
|
|
{
|
|
int w = grid.GetLength(0), h = grid.GetLength(1), count = 0;
|
|
for (int dx = -1; dx <= 1; dx++)
|
|
for (int dy = -1; dy <= 1; dy++)
|
|
{
|
|
if (dx == 0 && dy == 0) continue;
|
|
int nx = cx + dx, ny = cy + dy;
|
|
// Out-of-bounds counts as solid (closes cave edges naturally)
|
|
if (nx < 0 || nx >= w || ny < 0 || ny >= h) count++;
|
|
else if (grid[nx, ny]) count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
private static void EnforceBorder(bool[,] grid)
|
|
{
|
|
int w = grid.GetLength(0), h = grid.GetLength(1);
|
|
for (int x = 0; x < w; x++) { grid[x, 0] = true; grid[x, h - 1] = true; }
|
|
for (int y = 0; y < h; y++) { grid[0, y] = true; grid[w - 1, y] = true; }
|
|
}
|
|
}
|