feat(mapgen): add cellular-automata and drunkard-walk generators
This commit is contained in:
1 parent
eeb96205cf
commit
9076c6c729
2 files changed
+107
No files matched your search
@@ -0,0 +1,70 @@
|
||||
-- Cellular-automata caves. Seed the interior with random walls, then relax with
|
||||
-- the classic 4-5 rule so blobs of open space and rock emerge. Connectivity is
|
||||
-- enforced afterwards by the dungeon layer.
|
||||
|
||||
local Map = require("map")
|
||||
local Tiles = require("tiles")
|
||||
|
||||
local Cellular = {}
|
||||
|
||||
local function wallNeighbours(grid, width, height, x, y)
|
||||
local count = 0
|
||||
for dy = -1, 1 do
|
||||
for dx = -1, 1 do
|
||||
if dx ~= 0 or dy ~= 0 then
|
||||
local nx, ny = x + dx, y + dy
|
||||
if nx < 1 or ny < 1 or nx > width or ny > height then
|
||||
count = count + 1
|
||||
elseif grid[(ny - 1) * width + nx] then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function Cellular.generate(width, height, rng, options)
|
||||
options = options or {}
|
||||
local fillChance = options.fillChance or 45
|
||||
local steps = options.steps or 5
|
||||
local grid = {}
|
||||
for y = 1, height do
|
||||
for x = 1, width do
|
||||
local idx = (y - 1) * width + x
|
||||
if x == 1 or y == 1 or x == width or y == height then
|
||||
grid[idx] = true
|
||||
else
|
||||
grid[idx] = rng:range(1, 100) <= fillChance
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _ = 1, steps do
|
||||
local nextGrid = {}
|
||||
for y = 1, height do
|
||||
for x = 1, width do
|
||||
local idx = (y - 1) * width + x
|
||||
local walls = wallNeighbours(grid, width, height, x, y)
|
||||
if x == 1 or y == 1 or x == width or y == height then
|
||||
nextGrid[idx] = true
|
||||
else
|
||||
nextGrid[idx] = walls >= 5
|
||||
end
|
||||
end
|
||||
end
|
||||
grid = nextGrid
|
||||
end
|
||||
|
||||
local map = Map.new(width, height)
|
||||
for y = 1, height do
|
||||
for x = 1, width do
|
||||
if not grid[(y - 1) * width + x] then
|
||||
map:set(x, y, Tiles.FLOOR)
|
||||
end
|
||||
end
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
return Cellular
|
||||
@@ -0,0 +1,37 @@
|
||||
-- Drunkard's walk: a set of random walkers stumble through the rock carving
|
||||
-- floor as they go, until a target fraction of the interior is open. Produces
|
||||
-- sinuous, organic corridors distinct from the blobby caves of the CA generator.
|
||||
|
||||
local Map = require("map")
|
||||
local Tiles = require("tiles")
|
||||
|
||||
local Drunkard = {}
|
||||
|
||||
local STEPS = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }
|
||||
|
||||
function Drunkard.generate(width, height, rng, options)
|
||||
options = options or {}
|
||||
local coverage = options.coverage or 0.42
|
||||
local target = math.floor((width - 2) * (height - 2) * coverage)
|
||||
local map = Map.new(width, height)
|
||||
local x, y = width // 2, height // 2
|
||||
map:set(x, y, Tiles.FLOOR)
|
||||
local carved = 1
|
||||
local budget = width * height * 40
|
||||
|
||||
while carved < target and budget > 0 do
|
||||
budget = budget - 1
|
||||
local dir = STEPS[rng:range(1, 4)]
|
||||
local nx, ny = x + dir[1], y + dir[2]
|
||||
if nx >= 2 and ny >= 2 and nx <= width - 1 and ny <= height - 1 then
|
||||
x, y = nx, ny
|
||||
if map:get(x, y) ~= Tiles.FLOOR then
|
||||
map:set(x, y, Tiles.FLOOR)
|
||||
carved = carved + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
return Drunkard
|
||||
Reference in new issue
Block a user