my character was too big adjust the game setting to only allow smaller avatars or increase the door size. because of that i’m not really sure but i think that each of the rooms have 4 doors if you want more of a maze then i would make it have chances of different ones my friend made this a while a go not sure how it works but he gave it to me
Replicatedstorage, Module
-- Generates a maze grid and places room prefabs rotated to match door openings.
-- Assumes 50x50 room footprint and models in ReplicatedStorage.Rooms
-- Base direction convention (unrotated prefabs):
-- North = -Z, East = +X, South = +Z, West = -X
local RoomMaze = {}
local RS = game:GetService("ReplicatedStorage")
-- Bitmask flags for door openings
local DIR = { N = 1, E = 2, S = 4, W = 8 }
local ORDER = { "N", "E", "S", "W" } -- for rotation logic
-- Opposite direction lookup
local OPP = { [DIR.N]=DIR.S, [DIR.E]=DIR.W, [DIR.S]=DIR.N, [DIR.W]=DIR.E }
local bit32 = bit32 -- shorthand
-- Rotate a door bitmask 90° clockwise 'times' times
local function rotateMask(mask, times)
times = times % 4
if times == 0 then return mask end
local result = 0
-- map N->E->S->W->N using ORDER = { "N","E","S","W" }
for i, name in ipairs(ORDER) do
local bit = DIR[name]
if bit32.band(mask, bit) ~= 0 then
-- shift index by 'times', wrap 1..4
local newIndex = ((i - 1 + times) % 4) + 1
local newName = ORDER[newIndex]
result = bit32.bor(result, DIR[newName])
end
end
return result
end
-- Tile set: base model + its base mask (doors before rotation)
-- Only ONE of each needed; we rotate to fit any mask variant.
local function getTileSet(roomsFolder)
return {
{ name = "Room_1Door", baseMask = DIR.N }, -- N
{ name = "Room_2Door_Straight", baseMask = bit32.bor(DIR.N, DIR.S) }, -- N+S
{ name = "Room_2Door_Corner", baseMask = bit32.bor(DIR.N, DIR.E) }, -- N+E
{ name = "Room_3Door", baseMask = bit32.bor(DIR.N, DIR.E, DIR.S) }, -- N+E+S
{ name = "Room_4Door", baseMask = bit32.bor(DIR.N, DIR.E, DIR.S, DIR.W) }, -- N+E+S+W
}, roomsFolder
end
-- Find a prefab + rotation that matches a desired door mask
local function choosePrefabForMask(mask, roomsFolder)
local tileSet = getTileSet(roomsFolder)
-- Try most specific to least? We can just loop; 4Door will match only 15 anyway.
for _, entry in ipairs(tileSet) do
local model = roomsFolder:FindFirstChild(entry.name)
if model and model:IsA("Model") then
for rot = 0, 3 do
if rotateMask(entry.baseMask, rot) == mask then
return model, rot
end
end
end
end
-- Fallback: if mask somehow 0 (shouldn't happen), place a 1Door anyway.
local one = roomsFolder:FindFirstChild("Room_1Door")
if one then
return one, 0
end
warn("No matching prefab found for mask: ", mask)
return nil, 0
end
-- Shuffle helper for random traversal
local function shuffledDirs(rng)
local dirs = { DIR.N, DIR.E, DIR.S, DIR.W }
for i = #dirs, 2, -1 do
local j = rng:NextInteger(1, i)
dirs[i], dirs[j] = dirs[j], dirs[i]
end
return dirs
end
local bit32 = bit32
-- Generate a perfect maze: returns a grid of mask ints (open doors per cell)
local function carveMaze(width, height, rng)
-- masks[x][y] stores door openings bitmask for that cell
local masks = table.create(width)
for x = 1, width do
masks[x] = table.create(height, 0)
end
local visited = table.create(width)
for x = 1, width do
visited[x] = table.create(height, false)
end
local function inBounds(x, y)
return x >= 1 and x <= width and y >= 1 and y <= height
end
-- Neighbor step deltas by DIR
local DX = { [DIR.N]=0, [DIR.E]=1, [DIR.S]=0, [DIR.W]=-1 }
local DY = { [DIR.N]=-1,[DIR.E]=0, [DIR.S]=1, [DIR.W]= 0 }
-- Iterative DFS
local stack = {}
local function push(x,y) stack[#stack+1] = {x=x,y=y} end
local function pop() local n=stack[#stack]; stack[#stack]=nil; return n end
-- start anywhere (1,1) for consistency
push(1,1)
visited[1][1] = true
while #stack > 0 do
local node = stack[#stack]
local x, y = node.x, node.y
local progressed = false
for _, dir in ipairs(shuffledDirs(rng)) do
local nx, ny = x + DX[dir], y + DY[dir]
if inBounds(nx, ny) and not visited[nx][ny] then
visited[nx][ny] = true
-- open current cell's door in 'dir'
masks[x][y] = bit32.bor(masks[x][y], dir)
-- open neighbor's opposite door
masks[nx][ny] = bit32.bor(masks[nx][ny], OPP[dir])
push(nx, ny)
progressed = true
break
end
end
if not progressed then
pop()
end
end
return masks
end
-- Place a model clone at (gridX, gridY) with rotation rot (0..3) on Y-axis
local function placeRoom(container, prefab, originCF, roomSize, gridX, gridY, rot)
local clone = prefab:Clone()
if not clone.PrimaryPart then
warn(("PrimaryPart not set on prefab %s"):format(prefab.Name))
return nil
end
-- translate: cell (1,1) at origin; X grows +X, Y grows +Z
local offset = CFrame.new((gridX-1) * roomSize, 0, (gridY-1) * roomSize)
local rotation = CFrame.Angles(0, math.rad(90 * rot), 0)
clone:PivotTo(originCF * offset * rotation)
clone.Parent = container
return clone
end
-- Public API
-- params = {
-- gridW: number,
-- gridH: number,
-- roomSize: number (default 50),
-- originCFrame: CFrame (default CFrame.new()),
-- roomsFolder: Instance (default ReplicatedStorage.Rooms),
-- seed: number? (optional, for reproducible mazes),
-- extraLoops: integer (optional, adds random extra connections)
-- }
function RoomMaze.Generate(params)
local bit32 = bit32
local gridW = params.gridW or 20
local gridH = params.gridH or 20
local roomSize = params.roomSize or 50
local origin = params.originCFrame or CFrame.new()
local roomsFolder = params.roomsFolder or RS:WaitForChild("Rooms")
local seed = params.seed
local extraLoops = math.max(0, tonumber(params.extraLoops) or 0)
local rng = Random.new(seed or os.time())
-- Build door masks via DFS
local masks = carveMaze(gridW, gridH, rng)
-- Optionally add a few extra connections to create loops (less linear)
if extraLoops > 0 then
for _ = 1, extraLoops do
local x = rng:NextInteger(1, gridW)
local y = rng:NextInteger(1, gridH)
local dirs = shuffledDirs(rng)
-- try to open a new neighbor edge if closed
local DX = { [DIR.N]=0, [DIR.E]=1, [DIR.S]=0, [DIR.W]=-1 }
local DY = { [DIR.N]=-1,[DIR.E]=0, [DIR.S]=1, [DIR.W]= 0 }
for _, d in ipairs(dirs) do
local nx, ny = x + DX[d], y + DY[d]
if nx >= 1 and nx <= gridW and ny >= 1 and ny <= gridH then
-- was: if (masks[x][y] & d) == 0 then
if bit32.band(masks[x][y], d) == 0 then
-- was: masks[x][y] |= d
masks[x][y] = bit32.bor(masks[x][y], d)
-- was: masks[nx][ny] |= OPP[d]
masks[nx][ny] = bit32.bor(masks[nx][ny], OPP[d])
break
end
end
end
end
end
-- Container for placed rooms
local container = workspace:FindFirstChild("GeneratedRooms")
if container then container:Destroy() end
container = Instance.new("Folder")
container.Name = "GeneratedRooms"
container.Parent = workspace
-- Place each cell's room with the correct prefab & rotation
for y = 1, gridH do
for x = 1, gridW do
local mask = masks[x][y]
local prefab, rot = choosePrefabForMask(mask, roomsFolder)
if prefab then
placeRoom(container, prefab, origin, roomSize, x, y, rot)
else
warn("No prefab for cell", x, y, "mask", mask)
end
end
end
return container
end
return RoomMaze
Server
local RS = game:GetService("ReplicatedStorage")
-- Optional: origin where (1,1) cell sits
local origin = CFrame.new(0, 0, 0)
local RoomMaze = require(RS.RoomMaze.Generator)
RoomMaze.Generate({
gridW = 20, -- width in rooms
gridH = 20, -- height in rooms
roomSize = 50, -- your room footprint
originCFrame = origin,
roomsFolder = RS:WaitForChild("Rooms"),
seed = 12345, -- set for reproducibility; remove for random each run
extraLoops = 40, -- optional: adds loops so maze feels more “dungeon”
})
I tested it it works if it doesn’t its the pathing to it or models somthing like that