Back
MapMaker v1.0
RealBasic module by Bryan Winter
bryan@thewinternet.com

This Module will create a "dungeon" map of just about any width and height. It's pretty feature-packed, and generates some slick-looking dungeons. You are welcome to use this module for your own uses and projects. If you do anything cool I'd love a copy. And if you could give me a bit of credit that would be cool too. The best thing to do first is run the demo project and see how it works then come back here and check out the details.

One thing that you programmer-types may be interested in knowing is that this module does not use recursion to get the job done. I understand there have a been a few problems with recursion in RealBasic, but mostly I like the dungeons this module generates better. They tend not to have that "tree" look that recursion can cause. Rather, it's all random, from finding a candidate square to build from to placing a staircase, everything is done randomly. This makes the module slower than a recursive routine, but unless you are generating a really huge map the speed is negligible. That said, the really huge maps can take a while, but I don't think you could even get a huge map using a recursive routine (at least that is what I'm lead to believe). Regardless, I think these routines make better-looking dungeons. So that's what I did! :-)

To use it, you must perform the following method calls in order:

* * * * * * * * * * * * * * * * * * * * * *

First call SetGridSize(MapWidth as integer, MapHeight as integer)

This will set the width and height properties, plus it will redim the map arrays. It will also set the default properties. MapMidth and MapHeight are the number of cells you want in the map dimensions. The Minimum map size is 10 x10 (which results in some pretty dull maps, but your minimum has to start somewhere!) Theoretically, the map can be any maximum size, but the methods get pretty slow once you get over 200 x 200. Right now the imposed maximum is 400 x 400, which should be more than just about anyone needs.

There is a method call below that will let you set the Max size bigger if you really want.

* * * * * * * * * * * * * * * * * * * * * *

Now you can set the various properties *if you want*. They will default to the values below. I chose these default values because they tend to generate a well-balanced dungeon.

CorridorChance (integer)
The % chance that the method will try to create a corridor vs. a room. 0 means there is no chance for a corridor, the map will be all rooms. 100 means that the map will be all corridors. Note that it is more likely that the generation methods will place a corridor (since they are skinny), so an "even chance" number like 50 will generally yield more corridors than rooms.
MINIMUM VALUE: 0
MAXIMUM VALUE: 100
DEFAULT: 66

MinCorridorLength (integer)
The shortest a corridor can be.
MINIMUM VALUE: 1
MAXIMUM VALUE: MapWidth-4 or MapHeight-4, whichever is LOWER (-4 is to prevent problems at lower map sizes)
DEFAULT: 2

MaxCorridorLength (integer)
The longest a corridor can be.
MINIMUM VALUE: 1
MAXIMUM VALUE: MapWidth-4 or MapHeight-4, whichever is LOWER
DEFAULT: 8

Note: MinCorridorLength must be less than or equal to MaxCorridorLength.

CorridorDoorOffCorridorChance (integer)
The % chance that a corridor will start with a door when it "sprouts" off another corridor. Must be within 0 and 100 (inclusive). Note that doors are only created at the BEGINNING of a corridor (if they are created at all) otherwise you will get corridors that end with a door to nowhere. Often a corridor will "sprout" off another and start with a door. So it all works out in the end.
MINIMUM VALUE: 0
MAXIMUM VALUE: 100
DEFAULT: 50

CorridorDoorOffRoomChance (integer)
The % chance that a corridor will start with a door when it "sprouts" off a room.
MINIMUM VALUE: 0
MAXIMUM VALUE: 100
DEFAULT: 50

CorridorsEndWithWall (boolean)
A boolean to make sure a corridor does not "sprout" into another corridor or room. When TRUE, will tend to make the map more sprawling, for example a single corridor is the only route to a large area. When FALSE, the map will tend to be much more interconnected. You will also get corridors that diagonally attach to a room or corridor by a corner. The effects are more noticable on higher levels. Try the demo at level 100 (don't adjust any other controls) with this property set to true and false to see the difference.
DEFAULT: TRUE

CorridorsCanOpenIntoRoom (boolean)
A boolean that determines if a corridor can end opening into a room. When TRUE, the corridor can excavate into a room, and will NOT have a door. When FALSE, corridors will not end at rooms (but they can still begin from rooms). Note that if CorridorsEndWithWall is set to TRUE, this property has no effect, and will automatically be set to TRUE to "turn it off"
DEFAULT: TRUE
*** Basically all this property does is ensure all your rooms only have doors and not hallways running out of them, BUT ONLY IF CorridorDoorOffRoomChance, RoomDoorOffRoomChance AND RoomDoorOffCorridorChance ARE ALL SET TO 100. So this property is one you can usually ignore except for that special circumstance - Play with the demo to see what I mean.

MinRoomWidth (integer)
The thinnest a room can be.
MINIMUM VALUE: 1
MAXIMUM VALUE: MapWidth-4
DEFAULT: 3

MaxRoomWidth (integer)
The fatest a room can be.
MINIMUM VALUE: 1
MAXIMUM VALUE: MapWidth-4
DEFAULT: 8

Note: MinRoomWidth must be less than or equal to MaxRoomWidth.

MinRoomHeight (integer)
The shortest a room can be.
MINIMUM VALUE: 1
MAXIMUM VALUE: MapHeight-4
DEFAULT: 3

MaxRoomHeight (integer)
The tallest a room can be.
MINIMUM VALUE: 1
MAXIMUM VALUE: MapHeight-4
DEFAULT: 8

Note: MinRoomHeight must be less than or equal to MaxRoomHeight.

RoomDoorOffCorridorChance (integer)
The % chance that a room will start with a door when it "sprouts" off a corridor.
MINIMUM VALUE: 0
MAXIMUM VALUE: 100
DEFAULT: 50

RoomDoorOffRoomChance (integer)
The % chance that a room will start with a door when it "sprouts" off another room.
MINIMUM VALUE: 0
MAXIMUM VALUE: 100
DEFAULT: 50

NumStairsUp (integer)
The number of upward stairs to be placed. Must be at least 0. Must also be no more than one-tenth the smallest dimension of your map. So a 10x10 map can only have 1 stairs up (this is to prevent problems while searching for a place to put the stairs on *very* small maps).
MINIMUM VALUE: 0
MAXIMUM VALUE: MapWidth/10 or MapHeight/10, whichever is LOWER
DEFAULT: 2

NumStairsDown (integer)
The number of downward stairs to be placed. Must be at least 0. Must also be no more than one-tenth the smallest dimension of your map. So a 10x10 map can only have 1 stairs down (this is to prevent problems while searching for a place to put the stairs on *very* small maps).
MINIMUM VALUE: 0
MAXIMUM VALUE: MapWidth/10 or MapHeight/10, whichever is LOWER
DEFAULT: 2

* * * * * * * * * * * * * * * * * * * * * *

Finally, call MakeNewMap(Level as integer) as Boolean

This will generate the map. Level is the level number of the map. Higher numbers will create larger and more intricate maps. Must be 1 or higher. What is going on is that for every level number the MapMaker will TRY to place a room or corridor 50 times.

Note that MakeNewMap is a FUNCTION which will return a boolean if the map was successfully created or not. The only thing that will prevent the map from being created is if a property was set too high or too low. The ErrorChecking method will tell you if anything is wrong via MsgBoxes. Note that a map will not necessarity fill up the dimensions set aside for it. A low level number will often leave a large map area mostly empty.

* * * * * * * * * * * * * * * * * * * * * *

MakeNewMap will call these other methods so you don't have to worry about them. You shouldn't use these names for your methods.

ErrorChecking(level as integer) as Boolean
This method makes sure all your properties are set to legal values.

ExcavateCorridor()
This method does the work for creating a corridor in your dungeon.

ExcavateRoom()
This method does the work for creating a room in your dungeon.

FindFloor()
This method locates a candidate grid cell from which to build from. It ensures that every room and corridor in your dungeon is within reach (so you have no sections of the dungoen that are inaccessible).

PlaceStairs(whichway as string)
This method places a set of stairs in your dungeon.

* * * * * * * * * * * * * * * * * * * * * *

When finished, you will have an array of strings called MapGrid(x,y) containing your dungeon.

It is 0-based so the top-left corner is (0,0) and the bottom-right corner is (width-1, height-1).

Each cell in the map will be labeled one of these seven possible cell types:
"wall" - The entire grid is filled with wall when it is first created, and then the rest is excavated out of it.
"floor" - rooms are filled with floor.
"corridor" - hallways that are a single cell wide are labeled corridor (note that you can make rooms that are 1 cell wide as well - play with the demo to see).
"door" - Doors separate rooms and corridors from each other and themselves.
"archway" - An archway is created when a room is created and the passageway to that room is NOT a door.
"stairsdown" - A set of stairs down.
"stairsup" - A set of stairs up .

The outer edge:
[(0,0) to (0,width-1)]
[(0,0) to (O height-1)]
[(width-1,0) to (width-1,height-1)]
[(0, height-1) to (width-1,height-1)]
is always filled with "wall" - so it will be impossible for a player to move off the map.

* * * * * * * * * * * * * * * * * * * * * *

You can do whatever you want with these strings - even change them. The map grid this module generates is only to get you started. One example is if your player is allowed to dig through walls, you can set the outer border to something like "steel" so they can't dig themselves off the map. Or, you can cycle through the grid and do something with each door - turn it into a "gate" or make it a "lockedDoor". You can even create other grids based on the size of the MapGrid, and use the MapGrid to regulate all the action, like a MonsterGrid or an ItemGrid that you can use to populate your dungeon.

* * * * * * * * * * * * * * * * * * * * * *

You also have access to an array of booleans called FirstRoomTag(x,y). This array will track which room or corridor squares were first created. Only the initial room or corridor squares will be set to true (see demo). Originally this was a debugging thing but I kept it in just in case someone finds it useful.

* * * * * * * * * * * * * * * * * * * * * *

MapMaker also uses these properties. You should not change their values or use their names for your own properties.

CellsWide (integer)
How the module tracks the map width.

CellsHigh (integer)
How the module tracks the map height.

FoundDirection (string)
When the module finds a candidate square to build from, the direction to build in is stored here.

FoundX (integer)
When the module finds a candidate square to build from, the X value is stored here.

FoundY (integer)
When the module finds a candidate square to build from, the Y value is stored here.

SetMaxGridSize (integer)
Well, you can mess with this one - see below.

* * * * * * * * * * * * * * * * * * * * * *

Be sure to check out the Demo project. It shows off just about every aspect of the module. It's chock-full of controls, but all the interaction with the MapMaker module is happening in the demo's MakeAndDrawMap method (which is called when the demo opens and each time you click the MakeMap button). The slider interactions emphasize the importance of making sure your Min values are less than or equal to your Max values, and that everything stays within the map dimensions.

Note that the demo project is kinda hacked together so don't complain about the style. :-)

The Demo project is also a really good tool to use when figuring out how you want to set the module's properties. Since there are so many possibilities, play with the demo until you are generating maps that will work for you. It's set up with all the minimums and you can make a 400x400 maximum sized map, and will generally keep you from entering invalid numbers (but you can get around it sometimes and you will see some of the error messages). That maximums for the room and corridor sizes have only been set to 50 (they can be as high as 396, but that's a bit unwieldy). Remember that as the map gets really big, it can take a long time to generate. Of course, that's on my lowly 604 - your G4 may be a bit faster. :-)

One of the best features of the demo is that it wiull generate the RealBasic code for you! Play wth the settings until the module is creating maps that you like, then select File: Write Properties (Command-W). A window will appear containing all the code needed to create maps using the properties you have set. Simply copy and paste into your own projects.

You can also save maps you like - not very useful (I added it while I was teaching myself how to read and write files).

* * * * * * * * * * * * * * * * * * * * * *

If you really want to go nuts you can make the max map size bigger than 400 by setting the SetMaxGridSize property. You MUST set this property BEFORE you call SetGridSize for it to work. Just do this:

SetMaxGridSize = [integer]

The number you set it to must be over 400 for it to work.

If you do this you will have to update the demo project for it to work. First, change the maximum values of the sliders to accomodate. Because the canvas is 400 x 400 pixels, I'm pretty sure the image won't show up (and the app may likely crash) because it will create an image based on 0x0 pixel dimensions, so you will have to make the canvas bigger as well. So the canvas should be no smaller than the max grid size. You probably need to hack together a new drawing routine as well. :-)

* * * * * * * * * * * * * * * * * * * * * *

Well, that's it. I'm pretty pleased with this module and I hope you find it useful. I think it's pretty cool. Let me know what you think. I'm always happy to hear feedback - both good and bad!

And if you make any money off a program that uses it... cut me in! :-)

Happy programming,
Back
Bryan Winter
bryan@thewinternet.com