Complete roguelike tutorial using C++ and libtcod - part 3: dungeon building

From RogueBasin
Revision as of 11:48, 29 September 2015 by Joel Pera (talk | contribs) (pasted →‎The room creation)
Jump to navigation Jump to search
Complete roguelike tutorial using C++ and libtcod
-originally written by Jice
Text in this tutorial was released under the Creative Commons Attribution-ShareAlike 3.0 Unported and the GNU Free Documentation License (unversioned, with no invariant sections, front-cover texts, or back-cover texts) on 2015-09-21.


In this article, we will improve the Map class to generate a real dungeon with rooms and corridors. We'll use a very simple dungeon generator, using a straightforward way to connect rooms together. This will result in "winnable" dungeons (no room disconnected) but with a slightly chaotic look. For a better corridor connection, check the libtcod C++ samples in the libtcod/samples/ directory.

libtcod functions used in this article

TCODRandom::getInstance

TCODRandom::getInt

TCODBsp::TCODBsp

TCODBsp::splitRecursive

TCODBsp::traverseInvertedLevelOrder

A hole digging map

Whereas we were adding walls in an empty map in the last article, this time, we will dig holes in a map full of walls. We need to change the Map class declaration for that :

Tile() : canWalk(false) {}

Tiles not default to non-walking.

protected :
   Tile *tiles;
   friend class BspListener;

   void dig(int x1, int y1, int x2, int y2);
   void createRoom(bool first, int x1, int y1, int x2, int y2);
};

We replace the setWall function by a function that digs a rectangular zone. We also make a declaration that will allow some BspListener class to use the protected dig function. The BspListener class is not declared in the header because it's private to the Map class. The createRoom function will dig the room and populate it with actors.

The implementation

We define some constants for the room size range :

static const int ROOM_MAX_SIZE = 12;
static const int ROOM_MIN_SIZE = 6;

The static keyword, when used on a global variable means that the variable is not visible from outside the .cpp file.

The digging function

void Map::dig(int x1, int y1, int x2, int y2) {
   if ( x2 < x1 ) {
       int tmp=x2;
       x2=x1;
       x1=tmp;
   }
   if ( y2 < y1 ) {
       int tmp=y2;
       y2=y1;
       y1=tmp;
   }
   for (int tilex=x1; tilex <= x2; tilex++) {
       for (int tiley=y1; tiley <= y2; tiley++) {
           tiles[tilex+tiley*width].canWalk=true;
       }
   }
}

The only subtlety here is that we allow x2 to be smaller than x1 and y2 to be smaller than y1. That's why we swap the variables first to put them back in the right order. Being able to dig with *2 coordinates smaller than *1 will be handy for corridor digging.

The room creation

void Map::createRoom(bool first, int x1, int y1, int x2, int y2) {

   dig (x1,y1,x2,y2);
   if ( first ) {
       // put the player in the first room
       engine.player->x=(x1+x2)/2;
       engine.player->y=(y1+y2)/2;
   } else {
       TCODRandom *rng=TCODRandom::getInstance();
       if ( rng->getInt(0,3)==0 ) {
           engine.actors.push(new Actor((x1+x2)/2,(y1+y2)/2,'@',
               TCODColor::yellow));
       }
   }

} First we dig the room, then put either the player in its center (only for the first room) or some NPC in 25% of other rooms. We're using here libtcod's random number generator.