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