03. Creating more rooms¶
Now that we know the bare basics, it's time to create something more than a sad square. Let's create some more rooms!
Using Segment.createRoom¶
Segment.createRoom is the most basic way of creating another room in the segment. Let's start where we left off in the previous part:
local function myGenerator(genParams)
local instance = libLevelGen.new(genParams)
local mainSegment = instance:createSegment()
mainSegment:createStartingRoom()
instance:finalize()
end
createStartingRoom returns the created Room object.
-- In myGenerator(genParams):
local startingRoom = mainSegment:createStartingRoom()
local newRoomX = startingRoom.x
local newRoomY = startingRoom.y + startingRoom.h
local newRoom = mainSegment:createRoom(newRoomX, newRoomY, 5, 5)
You may notice that we specified the room to be 5x5, but the actual area with the floor is only 3x3 - that's because the width and height include the room border wall.
If you think that generating an entire level using only this would be immensely painful, you are absolutely correct - but don't worry, LibLevelGen has some more functions in store...
Segment.createRandLinkedRoom¶
Now we're entering stuff that's a bit more complicated. createRandLinkedRoom is probably the one most involved method in LibLevelGen, so we first need to get some background on what it actually does.
Suppose we want to create a room next to another room, connecting them with a corridor (which is also technically a room). We want the generated room to be next to the origin room in any direction, and we may also want corridor length to be randomized, corridor width to be randomized, room height and width to be randomized, etc... Since we're generating random levels here, we don't know too much about the context either - such as what other rooms might already be occupying some space where the room we want could possibly end up.
How many possibilities do we actually have here, and how do we check which ones are valid and won't cause any overlaps? We have to do all sorts of fun checks and calculations to make sure all is good, and it does not sound like a very good time. So, how about we let LibLevelGen do all of this for us?
Essentially, createRandLinkedRoom takes an origin room and a list of possible ways to generate it, then randomly picks a one that's valid and generates the room and the corridor (or not, if it doesn't find a one that's valid). But the list of all possible ways to generate a room... How do we actually specify it?
Creating RoomGenCombinations¶
So our goal here is to create a whole bunch of these tables, which define the ways in which createRandLinkedRoom is allowed to place the room. It'd be very inconvenient to do this by hand, so there's a helper function that can generate these for us: segment.createRandLinkedRoomParameterCombinations (that sure is a name!). Do note that it's a static function of the module and NOT a method of a Segment object. Let's take a look at how it's used:
local segment = require "LibLevelGen.Segment"
local room = require "LibLevelGen.Room"
-- The argument is similar to RoomGenCombination, but it uses tables
-- of numbers instead of single values.
local roomGenCombinations = segment.createRandLinkedRoomParameterCombinations {
-- Directions in which the room is allowed to generate.
direction = {room.Direction.UP, room.Direction.DOWN, room.Direction.LEFT, room.Direction.RIGHT},
-- Where corridor center is allowed to be relative to the origin room.
-- 0.5 is the middle, 0 is left/top and 1 is right or bottom.
corridorEntrance = {0.25, 0.5, 0.75},
-- Same as above, but relative to the generated room.
corridorExit = {0.25, 0.5, 0.75},
-- How thick the corridor is allowed to be. Do note that it takes the border
-- into account, so thickness of 3 is actually just 1-floor tile wide.
corridorThickness = {3},
-- How long is the corridor allowed to be.
-- Length of 0 is allowed, and will result in rooms being adjacent.
corridorLength = {0, 1, 2, 3, 4},
-- Allowed width values for the generated room.
roomWidth = {6, 7, 8, 9},
-- Allowed height values for the generated room.
roomHeight = {6, 7, 8, 9},
}
roomGenCombinations is all the ways in which the room is allowed to generate. In this case, it's 4*3*3*1*5*4*4 possible ways, which is a whopping 2880! We can verify that with a quick dbg(#roomGenCombinations):
Because of this, it's advised to only call this function once and then use the result. The easy way to do this is simply not putting it in any function, so that it'll only run when the mod is loaded and keep all the combinations ready.
Invoking createRandLinkedRoom¶
With our big table generated, we can finally go ahead and call the method. Here's the full file so far:
local libLevelGen = require "LibLevelGen.LibLevelGen"
local segment = require "LibLevelGen.Segment"
local room = require "LibLevelGen.Room"
local roomGenCombinations = segment.createRandLinkedRoomParameterCombinations {
direction = {room.Direction.UP, room.Direction.DOWN, room.Direction.LEFT, room.Direction.RIGHT},
corridorEntrance = {0.25, 0.5, 0.75},
corridorExit = {0.25, 0.5, 0.75},
corridorThickness = {3},
corridorLength = {0, 1, 2, 3, 4},
roomWidth = {6, 7, 8, 9},
roomHeight = {6, 7, 8, 9},
}
local function myGenerator(genParams)
local instance = libLevelGen.new(genParams)
local mainSegment = instance:createSegment()
local startingRoom = mainSegment:createStartingRoom()
-- The 2nd argument here defines whether the room should be
-- rotated when placed to the right/left of the room, essentially
-- swapping width and height.
-- Try setting roomWidth = {9}, roomHeight = {6} and then playing changing
-- this value to see what I mean here!
local newCorridor, newRoom, newRoomData = mainSegment:createRandLinkedRoom(startingRoom, false, roomGenCombinations)
instance:finalize()
end
libLevelGen.registerGenerator("LibLevelGen Playground", myGenerator)
Feel free to experiment with the values passed to createRandLinkedRoomParameterCombinations to get a better understanding of what they're doing!
And so that's basically createRandLinkedRoom in a nutshell. It might take some time to get used to, but it's also fun to mess around with.
Going even further¶
So with this, we can keep calling createRandLinkedRoom with the room we just generated as the 1st parameter, to generate more and more rooms in the level:
local startingRoom = mainSegment:createStartingRoom()
local currentRoom = startingRoom
for i = 1, 10, 1 do
local newCorridor, newRoom, newRoomData = mainSegment:createRandLinkedRoom(currentRoom, false, roomGenCombinations)
currentRoom = newRoom
end

This is certainly a level, but we can go even further and generate more than 1 connected room per room by using some fun recursion:
local function createTwoRooms(currentRoom, roomsLeft)
if roomsLeft > 0 then
local newCorridor1, newRoom1 = currentRoom.segment:createRandLinkedRoom(currentRoom, false, roomGenCombinations)
local newCorridor2, newRoom2 = currentRoom.segment:createRandLinkedRoom(currentRoom, false, roomGenCombinations)
-- Check if the generation didn't fail:
if newRoom1 then
createTwoRooms(newRoom1, roomsLeft - 1)
end
if newRoom2 then
createTwoRooms(newRoom2, roomsLeft - 1)
end
end
end
local function myGenerator(genParams)
local instance = libLevelGen.new(genParams)
local mainSegment = instance:createSegment()
local startingRoom = mainSegment:createStartingRoom()
createTwoRooms(startingRoom, 5)
instance:finalize()
end

...it is big. For extra silliness (and CPU torture) we can set the value to 10 instead of 5.

It is so big that the minimap can't fit it
And then, by playing with the combinations a bit...
{
direction = {room.Direction.UP, room.Direction.DOWN, room.Direction.LEFT, room.Direction.RIGHT},
corridorEntrance = {0.5},
corridorExit = {0.5},
corridorThickness = {3},
corridorLength = {0, 1, 2, 3, 4},
roomWidth = {3},
roomHeight = {3},
}
Perhaps we could change createTwoRooms to generate one more room once roomsLeft reaches 0, and use different combinations? This way, we could get some bigger rooms in there, surrounded by this maze of corridors! Feel free to try doing this as an exercise.
And that is it for now! Hopefully these examples gave you an idea of what createRandLinkedRoom is capable of - with some combinations and a bit of clever code, it can shape your entire level!