Geometry Rules
I decided to approach the rooms from a Quake-era mentality of level design. Rooms are cut-out from a solid. So, as we add shapes to a room's definition, we "cut" those shape out of the solid map. When we subtract shapes from the room, we fill those areas back in with solid walls.
Decoration Rules
Decorations can be much more complex. I've decided to start with a state stack. You can push as many "decor rules" as necessary and, when you run the placeDecor command, the rules will be evaluated and then the current state will be reset back to "identity". Decor rules include adding what decor(s) can be placed, how many and where they can be placed, and what method the room can use to select the decor to place - currently a round robin and random are implemented.
Procedurally created rooms.
Grammar Script Sample
Here's the script used to generate the above rooms:
$generateRoom {
// add two rectangles (one tall and thin, one shorter and wider)
?addGeometry [rect;3;6;5;8]
?addGeometry [rect;6;9;3;5]
// prepare to add beds (up to 2)
?addDecorRule [border;3;2;2;2]
?addDecorType [bed]
?placeDecor [2]
// prepare to add a chest south of each bed placed (up to 2)
?addDecorType [chest]
?addDecorRule [border;bed;0;0;0]
?placeDecor [2]
}
For every room created in the above image (except for the two rectangle samples), we add two rectangles. Then, we setup the rules for placing beds. The "border" rule describes the requirements around a valid place for the bed. In this case, the tile to the north of the bed must be a wall (3) and the tiles to the east, west, and south must be empty floor (3). This forces at least one tile to appear between two beds in a room. Last, we create the rules for the chests by setting up the "border" rule requiring a "bed" to the north of the chest and setting the other directions to "don't care" (0).
A note on grammar:
I'm currently using semi-colon to separate parameters in the grammar, but I'll probably change this before I release a version. I can see semi-colon being used frequently when the grammar is also used to create procedural room descriptions and/or narrative. To allow for this, I'm planning to switch to a pipe character as the parameter separator.
Longer term, I'd like to add a mechanism to use descriptors in place of numbers for parameters. So, you could say "?addDecorRule [border;wall;open;open;open]" instead of requiring knowledge of the enumerations in the code.
In closing
This ended up much longer than I intended, but my hope is that it's informative and/or inspiring. If you have any questions or comments, please feel free to contact me or leave a comment.