feat(mapgen): add room-and-corridor and bsp generators
This commit is contained in:
1 parent
e9cdb763ae
commit
eeb96205cf
2 files changed
+120
No files matched your search
@@ -0,0 +1,69 @@
|
|||||||
|
-- Binary space partitioning: recursively split the grid into a tree of regions,
|
||||||
|
-- carve one room per leaf, then connect each internal node's two children by a
|
||||||
|
-- corridor between the centres of a room drawn from each subtree.
|
||||||
|
|
||||||
|
local Map = require("map")
|
||||||
|
local Carve = require("gen.carve")
|
||||||
|
|
||||||
|
local Bsp = {}
|
||||||
|
|
||||||
|
local function split(node, rng, minLeaf, depth)
|
||||||
|
if depth <= 0 or (node.w < minLeaf * 2 and node.h < minLeaf * 2) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local horizontal
|
||||||
|
if node.w > node.h and node.w >= minLeaf * 2 then
|
||||||
|
horizontal = false
|
||||||
|
elseif node.h >= minLeaf * 2 then
|
||||||
|
horizontal = true
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if horizontal then
|
||||||
|
local cut = rng:range(minLeaf, node.h - minLeaf)
|
||||||
|
node.left = { x = node.x, y = node.y, w = node.w, h = cut }
|
||||||
|
node.right = { x = node.x, y = node.y + cut, w = node.w, h = node.h - cut }
|
||||||
|
else
|
||||||
|
local cut = rng:range(minLeaf, node.w - minLeaf)
|
||||||
|
node.left = { x = node.x, y = node.y, w = cut, h = node.h }
|
||||||
|
node.right = { x = node.x + cut, y = node.y, w = node.w - cut, h = node.h }
|
||||||
|
end
|
||||||
|
split(node.left, rng, minLeaf, depth - 1)
|
||||||
|
split(node.right, rng, minLeaf, depth - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function carveLeaves(node, map, rng)
|
||||||
|
if node.left or node.right then
|
||||||
|
carveLeaves(node.left, map, rng)
|
||||||
|
carveLeaves(node.right, map, rng)
|
||||||
|
local a = node.left.room
|
||||||
|
local b = node.right.room
|
||||||
|
local ax, ay = Carve.center(a)
|
||||||
|
local bx, by = Carve.center(b)
|
||||||
|
Carve.corridor(map, ax, ay, bx, by, rng)
|
||||||
|
node.room = a
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local w = rng:range(3, math.max(3, node.w - 2))
|
||||||
|
local h = rng:range(3, math.max(3, node.h - 2))
|
||||||
|
local x = node.x + rng:range(1, math.max(1, node.w - w - 1))
|
||||||
|
local y = node.y + rng:range(1, math.max(1, node.h - h - 1))
|
||||||
|
local room = { x = x, y = y, w = w, h = h }
|
||||||
|
Carve.rect(map, room.x, room.y, room.w, room.h)
|
||||||
|
map.rooms[#map.rooms + 1] = room
|
||||||
|
node.room = room
|
||||||
|
end
|
||||||
|
|
||||||
|
function Bsp.generate(width, height, rng, options)
|
||||||
|
options = options or {}
|
||||||
|
local minLeaf = options.minLeaf or 7
|
||||||
|
local maxDepth = options.maxDepth or 5
|
||||||
|
local map = Map.new(width, height)
|
||||||
|
local root = { x = 2, y = 2, w = width - 3, h = height - 3 }
|
||||||
|
split(root, rng, minLeaf, maxDepth)
|
||||||
|
carveLeaves(root, map, rng)
|
||||||
|
return map
|
||||||
|
end
|
||||||
|
|
||||||
|
return Bsp
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
-- Non-overlapping rectangular rooms joined by L-shaped corridors. Returns a
|
||||||
|
-- carved Map with map.rooms populated; stair, door and trap placement is the
|
||||||
|
-- dungeon layer's job, not the generator's.
|
||||||
|
|
||||||
|
local Map = require("map")
|
||||||
|
local Carve = require("gen.carve")
|
||||||
|
|
||||||
|
local Rooms = {}
|
||||||
|
|
||||||
|
local function overlaps(a, b)
|
||||||
|
return a.x <= b.x + b.w and a.x + a.w >= b.x
|
||||||
|
and a.y <= b.y + b.h and a.y + a.h >= b.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function Rooms.generate(width, height, rng, options)
|
||||||
|
options = options or {}
|
||||||
|
local maxRooms = options.maxRooms or 14
|
||||||
|
local minSize = options.minRoomSize or 4
|
||||||
|
local maxSize = options.maxRoomSize or 9
|
||||||
|
local map = Map.new(width, height)
|
||||||
|
|
||||||
|
for _ = 1, maxRooms do
|
||||||
|
local w = rng:range(minSize, maxSize)
|
||||||
|
local h = rng:range(minSize, maxSize)
|
||||||
|
local room = {
|
||||||
|
x = rng:range(2, width - w - 1),
|
||||||
|
y = rng:range(2, height - h - 1),
|
||||||
|
w = w, h = h,
|
||||||
|
}
|
||||||
|
local fits = true
|
||||||
|
for _, other in ipairs(map.rooms) do
|
||||||
|
if overlaps(room, other) then
|
||||||
|
fits = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if fits then
|
||||||
|
Carve.rect(map, room.x, room.y, room.w, room.h)
|
||||||
|
if #map.rooms > 0 then
|
||||||
|
local prevX, prevY = Carve.center(map.rooms[#map.rooms])
|
||||||
|
local newX, newY = Carve.center(room)
|
||||||
|
Carve.corridor(map, prevX, prevY, newX, newY, rng)
|
||||||
|
end
|
||||||
|
map.rooms[#map.rooms + 1] = room
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return map
|
||||||
|
end
|
||||||
|
|
||||||
|
return Rooms
|
||||||
Reference in new issue
Block a user