Skip to main content

ProcGen

local ProcGen = Quark.ProcGen

local cave = ProcGen.Caves(120, 80, { Generator = Quark.Random.New("cave-" .. level) })
local dungeon = ProcGen.Dungeon(100, 60, { MaxRoomSize = 12 })
local heights = ProcGen.Heightmap(256, 256, { Fractal = "FBm", Island = true, Max = 3000 })

Procedural generation of tile maps and terrain. All generators take an optional Generator from Random.New, the same seed always produces the same map. Cells are addressed starting at the index base of the package, like pathfinding grids.

Tile maps​

ProcGen.Caves(width, height, options?) grows organic caves with a cellular automaton: cells start as random walls and floors and become walls when most neighbours are walls. The border is always wall.

Option
FillChanceshare of initial walls (0.45), more gives narrower caves
Stepssmoothing steps (5)
BirthLimit, DeathLimita floor becomes wall with more than BirthLimit wall neighbours (4), a wall becomes floor with fewer than DeathLimit (4)
KeepLargestfill all caves except the largest, so every floor cell is reachable (true)
Generatorrandom generator

ProcGen.Dungeon(width, height, options?) places rectangular rooms without overlap and connects them with corridors along a minimum spanning tree, so all rooms are reachable, plus some extra corridors for loops.

Option
Attemptshow often a room is tried (60), more gives more rooms
MinRoomSize, MaxRoomSizeroom sizes in cells (4 and 10)
Loopsexpected number of extra connections per room for alternative routes (1), 0 for a tree
Doorsmark where corridors enter rooms (true)
Generatorrandom generator

Both return a map. Tiles are numbers, ProcGen.Tile holds their names: Wall (0), Floor (1), Corridor (2) and Door (3).

Method
:GetSize()width and height
:GetTile(x, y), :SetTile(x, y, tile), :IsWalkable(x, y)
:GetRooms()rooms of dungeons as { X, Y, Width, Height, Center = { X, Y } }
:GetFloorCells()all walkable cells as { X, Y }, for example to pick spawn points
:GetTiles()all tiles as a buffer of bytes, row by row
:ToCosts()costs for Pathfinding.Grid :LoadCosts, walls blocked
:ToText()text picture with # walls, . floors, , corridors and + doors, handy for debugging
local map = ProcGen.Dungeon(64, 64)
local grid = Quark.Pathfinding.Grid(64, 64, { CellSize = 400 })
grid:LoadCosts(map:ToCosts())

for y = 0, 63 do
for x = 0, 63 do
if not map:IsWalkable(x, y) then
StaticMesh(grid:ToWorld(x, y), Rotator(), "nanos-world::SM_Cube"):SetScale(Vector(4, 4, 4))
end
end
end

Heightmaps​

ProcGen.Heightmap(width, height, options?, callback?) returns a buffer of width * height little endian 32 bit floats, row by row, read it with buffer:View("f32"). It takes all options of Random.Noise and these:

Option
Min, Maxheights are scaled to this range (0 and 1)
Stepnoise distance between neighbouring cells (1), combine it with Frequency
Islandlower the edges, so the map becomes an island surrounded by its lowest height
Erosion, Talusiterations of thermal erosion that flattens steep slopes (0), and the slope that stays (0.01 of the height range)

With a callback the heightmap is generated on a worker thread.