NetHack Level Generation, Part 3
We’re back in makelevel(), at line 666. The rooms are placed, but they’re empty.
The one thing that every level needs are the stairs that will connect it to the levels above and below it. Unlike some other roguelikes with many stairs between levels, NetHack levels always have exactly one up stair, and every level except the bottom one also has one down stair. Plus, there is the possibility of a second down stair to one of the special branches of the dungeon.
To create the stairs, first pick a random room. If this isn’t the bottom level, place the down stair at a random location in this room. Now, check to see if there is more than one room. NetHack prefers to place the stairs in different rooms, so if there are at least two rooms, it picks a new random room. This time, instead of picking from all of the rooms, it picks from any room except the last one. This is in case it randomly picks the same room that the down stair is already in, in which case it picks the room with the next highest number.
Whichever room it has selected, it places the up stairs in a random, unoccupied location within that room, by randomly generating coordinates in a while loop until it finds one that returns false for occupied(). At this point, “occupied” mostly means “the down stairs are in this tile”. (This is terribly inefficient, but seldom matters.)
Then it checks to see if this level has a connection to a dungeon branch. This is where room_threshold finally gets set, with the ternary operator. If there is a branch, the threshold is 4; otherwise it is 3.
(If this is the special Rogue level, there’s a goto command here, to skip over this next bit. I told you this code was gnarly.)
The rooms are now connected with corridors via makecorridors().
First, it loops through the array of rooms, trying to join each room to the next one in the sequence. After each join, there’s a 2% chance that it’ll stop and go to the second step.
Second, it loops through the list again, this time trying to join each room to the room two steps down the array.
Third, it goes through the list of rooms, trying to join each room to every other room, but stopping once it runs out of rooms that aren’t the current room (e.g. if there’s only one room).
Finally, if there are more than two rooms, it picks a random number (based on the number of rooms), adds 4 to it, and then counts down, each loop attempting to link two random rooms. To keep the randomly selected rooms from being the same, it uses the same trick as with the stairs: a is random(number of rooms) and b is random(number of rooms minus two). If b is equal to or greater than a, add two to b.
As you can see, it attempts to add connections between nearly every room, so what is important here is the order the joins are attempted; join() tries to find places to put doors and then tries to dig corridors between them. It doesn’t always succeed, which the algorithm takes advantage of. Enough corridors are placed to result in NetHack’s characteristic look of rooms suspended in a sea of tunnels.
Once the corridors are placed, makelevel() calls make_niches() to add closets to the level, which are 1-square corridors-rooms that will either be connected to a room with a door (often a secret door) or which can only be reached by digging or teleportation.
For the number of niches, it picks a number between 0 and the number of rooms bitshifted to the right plus 1: rnd((nroom>>1) + 1). It also checks the depth: between level 5 and level 25 (noninclusive) there’s a chance for a trap door. And if the depth is greater than 15 and it’s a no-teleport level, there won’t be a teleport-trap niche.
Niche generation is handled by makeniche().
As long as the level hasn’t already exceeded DOORMAX, it runs a loop 8 times, picking a random room each time and attempting to place a connected niche. There’s some restrictions on where they’re allowed: only in ordinary rooms, only if there’s space to place it, etc.
If it can place a niche, it checks the requested trap type, and sets up the niche accordingly. Some traps get engravings (trap doors get “Vlad was here”).
Once there have been eight attempts at placing the niche, it finishes and returns, where we resume execution in makelevel().
Next time, we’ll talk about adding special rooms and secret treasure vaults.