Making a Roguelike Game in C++ with BearLibTerminal

>
  1. Introdcution
  2. Hello, world!
  3. Part 1: Creating "@" and moving it around
  4. Part 2: Making a generic Entity class
  5. Part 3: Procedurally Generated Dungeons
  6. Part 4: FOV
  7. Part 5: Knowing our direction, Enter: bad guys
  8. Part 6: The graphical user interface
    1. Adding a clock
    2. Progress bars
    3. Blocking vs. Not blocking
  9. Part 7: Fighting monsters that fight back.(with damage!)
  10. Interlude: Spiffing up our map with tilesets
  11. Part 8: Adding Items and an inventory management system.

>

Introduction

When it comes to developing a Roguelike game, the choices of language and supportive libraries are myriad. By far the most popular choice seems to be libtcod with its extensive documentation and portage to many languages. Another contender for most popular is the newer BearLibTerminal, and while it doesnt have all the bells and whistles that the doryen library does in terms of Algorithims, multiple rendering options, etc, it more than makes up for in ease of deployment and some what more relaxed syntax. That being said, strong documentation is not one of BearLibTerminal's strong points. But before i talk you out of it, lets get busy coding, and see what we can shake up.




Hello, world!

Just to give you a quick peak at how simple using this library is, lets do a quick hello world project to stretch the fingers and make sure our coding environment is set up and working. I wont go into a long diatribe about IDE's and such because i think thats outside the scope of this tutorial for the most part, but just for the sake of mentioning it, i did all of the coding for this tutorial in a rather no frills IDE, (Atom) and used the GNU C++ compiler from the command prompt on a mac book. I happen to know that all of the examples in this tutorial will compile and run on a Linux computer with no changes. Aside from G++, and something to type code in, the only other thing you will nee is BearLibTerminal itsself
which you can get: Here
aside from the actual library
the aformentioned site seems to be the only place on all of the internet that has ANY kind of Documentation
related to actually using the library, something im actively trying to change with this tutorial.

That being said, lets get started.

File Structure

For this project all thats needed is to make a folder to hold your source files, as well "BearLibTerminal.h" and the compiled library, on a mac that would be libBearLibTerminal.dylib. Moving right along we want to create a new file and save it as main.cpp. Using our text editor/IDE of choice the first thing we're going to is add the following code to main.cpp

hello.cpp
#include "BearLibTermina.h"
int main(int argc, char *argv[])
{
    int key;
    terminal_open();
    terminal_set("window: title='BearLib C++ tutorial', size=80x40");
    terminal_print(20, 20, "Hello, World!");
    terminal_refresh();
    while (key = terminal_read() != TK_Q)     {

    }
    terminal_close();
    return 0;
}
Not Bad! less than ten lines of code, im assuming you didnt sprain any finger muscles typing all that out, so pop over to the command line and type the following:
g++ libBearLibTerminal.dylib main.cpp -o main; ./main
Your code should compile with no errors, and you should be presented with:

Ok, so we're set up, codes compiling, programs are running, lets take a second to go over the functions we used.

terminal_open()
tells the library that our program wants to use a terminal window for interacting with the user. Despite its name, this is not the function call which makes the window visible to us.
terminal_set()
passes configuration parameters to the library for the terminal as a string, in this case giving the window a title, and setting the terminal size to 80 characters wide by 40 characters tall.
terminal_print()
takes an x coordinate, a y coordinate, and the string to be displayed, starting at the coordinate provided. If your raising your hand to tell me that an x/y value is a point and not a coordinate, you can put your hand down. i know. and that kind of attitude wont win you any friends.
terminal_refresh()
THIS is the function call that tells the library that our window has been configured and is ready to be displayed. The while () loop keeps our program open and running so long as you dont press the "q" key. More on this in a moment.
terminal_close()
does a little memory management, and thats our hello world program.

:: Sips Coffee :: Since Hello World is the first example in just about every "how to code blah blah" ever written (hi Kernighan!) We'll stick with tradition and do the essential Roguelike first task.




Introducing:

@

Open main.cpp back up and make the following changes:
  • at the top of int main() add: int playerx = 20, playery = 20;
  • in the while() loop add:
  • game loop:
    switch (key)
    {
        case TK_UP: playery--; break;
        case TK_DOWN: playery++; break;
        case TK_RIGHT: playerx++; break;
        case TK_LEFT: playerx--; break;
        default: break;
    }
    terminal_clear();
    terminal_print(playerx, playery, "@");
    terminal_refresh();

Ok, nothing crazy here. aside from terminal_clear() we've seen
all of these functions. were using a switch/case on the value of 'key'
which is where our call to terminal_read() stores its output as an
integer representation of which key on the keyboard was pressed. the
/ BearLibTerminal gives us a hand with the TK_* definitions, so that when one
of the directional arrows on your keyboard is pressed the switch routes
our program to take the cooresponding action. We call terminal_clear()
before redrawing the screen otherwise we'd leave a trail of '@' symbols.
There are several ways to handle this, and in fact we'll be changing strategies
soon, but for now it works.

Part1 Code:

#include "BearLibTerminal.h"

int main(int argc, char *argv[])
{
   int key,playerx = 7,playery = 7;
   terminal_open();
   terminal_set("window: title='BearLib C++ tutorial', size=80x40");
   terminal_layer(1);
   terminal_print(20, 20, "Hello, World!");
   terminal_print(7, 7, "@");
   terminal_refresh();
   while ((key = terminal_read()) != TK_Q) {
      switch (key) {
      case TK_UP:
         playery--;
         break;
      case TK_DOWN:
         playery++;
         break;
      case TK_RIGHT:
         playerx++;
         break;
      case TK_LEFT:
         playerx--;
         break;
      default:
         break;
      }
      terminal_clear();
      terminal_print(playerx, playery, "@");
      terminal_refresh();
   }
   terminal_close();
   return 0;
}

Making a Class for Game characters



!!!!! NOTE !!!! THIS SECTION IS INCOMPLETE AND STILL UNDER CONSTRUCTION!
FULL SOURCE CODE FOR THIS PARTS 1, 2, AND 3, IS AVAILABLE HERE FOR A BETTER
UNDERSTANDING OF THIS SECTION UNTILL I CLEAN IT UP

As our game grows and becomes populated by more "characters" than just the player, as any game besides tetris must
We'll need an efficient way of cataloging/controling/displaying etc each character. The simplest way to do this is to
create an object represent the characters by using classes. Pulling a page from /u/Tstand90's book, we'll refer to these
as Entities, or "Ents". Start by adding two new files to our source tree, ent.h and ent.cpp. In ent.h, outline the class
for the ent, by adding the following:

ent.h
class Ent {
public:
int px;
int py;
char symb[2];
void move(Map map, int x, int y);
Ent(int, int, char);
};

Simple enough, we have int px, int py to represent the characters x/y position on the map.
we have symb[2] to hold the char that will represent the ent on the map, we use char symb[2]
instead of char symb; even though were just using one char because terminal_print requires a string
and thus quietly accepts a character array but not a single char for output (bug? feature?) and lastly
a function, move() to change the associated x/y of the ent, and the constructor for the Ent class.
with that taken care of we can now add said functions to ent.cpp:

ent.cpp
Ent::Ent(int x, int y, char ch)
{
    px = x;
    py = y;
    ch[0] = ch
}

void Ent::move(Map map, int x, int y)
{
     px += x;
     px += y;
}



Now we want to make some changes to main.cpp, notably we are going to put the gameloop in its own function,
change the way that data is altered, and change the way everything is displayed. We're also going to add
two more files to our source tree, map.h and map.cpp, which will be used much more in the next section
in dungeon.h, define a class called Map:

map.h
class Map {
    int mapWidth;
    int mapHeight;
    int maxrooms;
};

Don't fret about that too much right now, more will be explained in Part 3..
In main.cpp, make a new function named gameLoop(); remember the while(keyboard_read()) Loop from part 1?
well, that whole chunk of code is being moved from int main(), to void gameLoop(). We're algo going to
change our player variables (int playerx, int playery) to utilize the entitiy class for our character player
and finally, add a render function to main.cpp to utilize give us better control over drawing everything
on the terminal. after implementing those changes your main.cpp should look like this:

drawAll(), our new render function
void drawAll(Map dungeon, Entity player)
{
int x, y;
terminal_layer(1);
for (y = 5; y < 40; y++) {
for (x = 0; x < 70; x++) {
if (dungeon.layout[x][y].blocking == false)
{
terminal_color("darker red");
terminal_print(x , y, ".");
}
if (dungeon.layout[x][y].borders == true) {
terminal_color("dark grey");
terminal_print(x, y, "#");
}
}
}
terminal_layer(2);
terminal_color("purple");
terminal_print(player.px, player.py, player.ch);
terminal_refresh();
}
gameLoop function:
void gameLoop() {
Map dungeon(40, 70, 20, false);
Entity player(dungeon.spx, dungeon.spy, '@');
drawAll(dungeon, player);
int key,playerx = 7,playery = 7;
while ((key = terminal_read()) != TK_Q) {
switch (key) {
case TK_UP:
player.move(dungeon, 0, -1);
break;
case TK_DOWN:
player.move(dungeon, 0, 1);
break;
case TK_RIGHT:
player.move(dungeon, 1, 0);
break;
case TK_LEFT:
player.move(dungeon, -1, 0);
break;
default:
break;
}
terminal_clear();
drawAll(dungeon, player);
}
}
int main()
int main(int argc, char *argv[])
{

srand(time(NULL));
terminal_open();
terminal_set("window: title='BearLib C++ tutorial', size=80x40");
terminal_layer(1);
gameLoop();
terminal_close();
return 0;
}



Procedurally Generated Dungeons

*NOTE: this section was update 7/6/2020 with a new code for a better looking dungeon algorithim. if anywhere in the text
i refer to map.cpp or map.h by a different name, or use a different name for a function than what is in the code, the code is correct
the text. if i did miss anything, please email me

So now that we have a basic core of a moving character lets give
the character something to explore. A fundamental aspect of Roguelike
games is that they consist of procedurally generated content, especially
dungeons/caves. Pages and pages of articles and papers have covered this
topic, and it really is quite interesting. For the sake of this tutorial
we are going to implement a very basc algorithim to generate our dungeon
but i HIGHLY suggest taking a look at some of the resources im including at
the end of this section.

The class system provided by C++ lends itself perfectly to several aspects of Roguelike
development, and map generation is one such area. The approach we're going to take is to
create a heirachy of ever increasingly complex Data structures. These data structures will
each have their own class, so go ahead open the files we added to our project in the previous section,
map.h and map.cpp. I'm assuming that
anyone following this tutorial has at least a modest understanding of C++, but for those who's
grasp isnt all together solid, or perhaps has faded over time(and possibly even corrupted by python)
the .h header file will be for declaring/outlining the classes and methods, and the .cpp will
be for their implementation.

Parts of a dungeon

The three basic units we're ar going to use to generate our dungeon are:
  • Tiles - the smallest unit of which all others are comprised of
  • Rooms - groupings of tiles congregated together to make a larger contiguos area
  • and the Dungeon, comprised of a series of Rooms, connected by,
  • Cooridors, also made of tiles

Ok, four basic units, but the cooridors are kind of a throw on, as youll come to see below.

So in map.h add the following piece of code:

map.h

Point struct
struct Point {
int x;
int y;
bool operator==(const Point& other) const {
return x == other.x && y ==other.y;
}
};
Tile struct
struct Tiles {
Point pos;
int isinRoom = 0;
bool blocks = true;
bool border = false;
};
Rect class
class Rect {
public:
Point uL;
Point lR;
Point cent;
int width;
int height;
int idNum;
bool collision(Rect other);
Rect(int x, int y, int w, int h);
Rect();
};
Map class
class Map {
public:
int spx, spy;
int width;
int height;
Tiles layout[80][40];
std::vector scrSect;
std::vector rooms;
void digRect(Rect room);
void genRect(int numRoom, int maxSize);
void connectRooms(std::vector);
void connectRooms2(std::vector);
void outline();
Map (int w, int h);
};

Allright now were getting somewhere, put up a pot of coffee cause we got a wee bit of coding ahead of us
while the coffee's brewing lets open up dungeon.cpp and start filling in some of the basics
Dammit. i almost forgot. create one more file, and name it helpers.h, were just going to add
a couple 'down home' functions that will make our lives easier in the up coming coding

helpers.h
#include <random>

//returns smaller number
int min(int a, int b)
{
  if (a < b) {
   return a;
  } else{
   return b;
  }
}

//returns bigger number
int max(int a, int b)
{
  if (a > b) {
    return a;
   } else {
    return b;
 n }
}

b
//random number in range.
int getrand(int min, int max)
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution distrib(min, max);
return distrib(gen);
}

You might be saying, but what about std::min and std::max? why reinvent the wheel? first, this min/max is not belonging to any.
particular namespace, and since relying on 'using namespace std' can complicate matters if you want to add other libraries
then this simplifies access to those two function(who's use comes up a fair bit in a project like this). second, the std::
versions of min and max have more functionality than we need, and thus doing this incurs less overhead. however, if you'd
prefer using whats in the standard library, by all means do so.
Open main.cpp, and right before the Game Loop begins add the following lines:

main.cpp additions
int mapWidth = 70;
int mapHeight = 35;
int maxRooms = 23;
Map gameMap(mapWidth, mapHeight);



Since thats taken care of, lets go over to map.cpp and add the methods we defined in map.h earlier.
First, lets get the Room class implemented and out of the way:


Room class constructor and functions: map.cpp

Rect::Rect(int x, int y, int w, int h)
{
this->uL.x = x;
this->uL.y = y;
this->lR.x = x + w;
this->lR.y = y + h;
}

Rect::Rect()
{

}
bool Rect::collision(Rect other)
{
if ((uL.x < other.lR.x) && (lR.x > other.uL.x) &&
(uL.y < other.lR.y) && (lR.y > other.uL.y)) {
return true; //collision
} else {
return false;
}
}

Ok, So first up is the Constructor for Room, its passed a starting point, located at its upper left corner (uL.x, uL.y) a width, and height.
With that information were able to come calculate the point at its lower right corner from the starting point(lR.x, lR.y)
Thats all we need to know to be able to "draw" a rectangle. The function bool collid(Rect other)
is used to check whether the Room we're currently working on is overlapping a previously created
Room. the Cent point is the "center" of the rectangle. I put "center" in quotes because
since were using int's, its actually off a bit from the center.
Our dungeon will be comprised of these rooms, sometimes connected by paths, sometimes as neighbors.
Now, lets take a look at the Constructor for the dungeon (called Map here cause im too lazy to type dungeon over and over)

Map class constructor: map.cpp
Map::Map(int w, int h)
{
int x, y;
this->width = w;
this->height = h;
//initialize tile grid
for (x = 1; x < w - 1; x++)
{
for (y = 1; y < h - 1; y++)
{
layout[x][y].blocks = true;
layout[x][y].pos.x = x;
layout[x][y].pos.y = y;
}
}
}

A quick run down of the constuctor. I wont insult your intelligence by explaining what mapHeight, and mapWidth represent.
the for loop goes through our array of tiles and sets all of their default values, this we way we dont get any whacky
results from assuming a value was set when it wasnt

I'm not going to provide an indepth analysis of the algorithm we are about to use to generate the dungeon, as that is
beyond the scope of this tutorial but ill give you the brief version:

  • using a random number generator, generate a random x/y point, and generate a random width and height.
  • if this is the first room generated, create it and add it to the vector, if its NOT the first room, use a loop to
    to check if it collides with previously generated rooms.
  • if it passes our test, create the room and add it to our rooms vector. otherwise, toss it and try again with a new set of
    randomly generated values. repeat untill we have generated numRooms == maxRooms
  • every time a room is deemed acceptable, we create tunnels branching out from the center point of the room. we randomly choose 50/50 whether
    we are going to branch out horizontally first, and then verticaly, or vice-versa.


Dungeon generating algorithim functions: map.cpp
Tunnel digging functions:
void Map::connectRooms(std::vector rooms)
{
int ax,bx;
int ay,by;
Rect* start;
Rect* fin;
int i = 0;
int r;
for ( r = 0; r < rooms.size() - 1; r++)
{
start = &rooms[i];
fin = &rooms[i+1];
std::cout<<"Connecting: "<idNum<<" to "<idNum<<"\n";
if (start->cent.x <= fin->cent.x)
{
std::cout<<"if one, condition A\n";
for (ax = start->cent.x; ax <= (fin->cent.x + start->cent.x) / 2; ax++)
{
layout[ax][start->cent.y].blocks = false;
layout[ax][start->cent.y].isinRoom = 77;
}
for (bx = fin->cent.x; bx >= (fin->cent.x + start->cent.x) / 2; bx--)
{
layout[bx][fin->cent.y].blocks = false;
layout[bx][fin->cent.y].isinRoom = 77;
}
std::cout<<"From: X"<cent.x<<" and "<cent.x<<" to "<<(start->cent.x + fin->cent.x)/2<<"\n";
} else {
std::cout<<"If one condition B\n";
for (ax = start->cent.x; ax >= (fin->cent.x + start->cent.x) / 2; ax--)
{
layout[ax][start->cent.y].blocks = false;
layout[ax][start->cent.y].isinRoom = 77;
}
for (bx = fin->cent.x; bx <= (fin->cent.x + start->cent.x) / 2; bx++)
{
layout[bx][fin->cent.y].blocks = false;
layout[bx][fin->cent.y].isinRoom = 77;
}
std::cout<<"From: X"<cent.x<<" and "<cent.x<<" to "<<(start->cent.x + fin->cent.x)/2<<"\n";
}
if (start->cent.y <= fin->cent.y) {
std::cout<<"if two condition A\n";
for (ay = start->cent.y; ay < fin->cent.y; ay++)
{
layout[ax][ay].blocks = false;
layout[ax][ay].isinRoom = 77; //77 =pathway
}
std::cout<<"From: Y"<cent.y<<" to "<cent.y<<"\n";
} else {
std::cout<<"if two, codition B\n";
for (by = fin->cent.y; by <= start->cent.y; by++)
{
layout[bx][by].blocks = false;
layout[bx][by].isinRoom = 77;
}
std::cout<<"From: Y"<cent.y<<" to "<cent.y<<"\n";
}
std::cout<<"Connected.\n";
i++;
}

}


void Map::connectRooms2(std::vector rooms)
{
int ax, ay;
int bx, by;
Rect* start;
Rect* fin;
int r, i=0;
for (r = 0; r < rooms.size() - 1; r++)
{
start = &rooms[i];
fin = &rooms[i] + 1;
std::cout<<"Connecting: "<idNum<<" to "<idNum<<"\n";
if (start->cent.y <= fin->cent.y)
{
std::cout<<"If one, condition A\n";
for (ay = start->cent.y; ay <= (start->cent.y + fin->cent.y) / 2; ay++)
{
layout[start->cent.x][ay].blocks = false;
layout[start->cent.x][ay].isinRoom = 77;
}
for (by = fin->cent.y; by >= (start->cent.y + fin->cent.y) / 2; by--)
{
layout[fin->cent.x][by].blocks = false;
layout[fin->cent.x][by].isinRoom = 77;
}
} else {
std::cout<<"If one, condition B\n";
for (ay = start->cent.y; ay >= (start->cent.y + fin->cent.y) / 2; ay--)
{
layout[start->cent.x][ay].blocks = false;
layout[start->cent.x][ay].isinRoom = 77;
}
for (by = fin->cent.y; by <= (start->cent.y + fin->cent.y) / 2; by++)
{
layout[fin->cent.x][by].blocks = false;
layout[fin->cent.x][by].isinRoom = 77;
}
}
if (start->cent.x <= fin->cent.x)
{
std::cout<<"If two, condition A\n";
for (ax = start->cent.x; ax <= fin->cent.x; ax++)
{
layout[ax][ay].blocks = false;
layout[ax][ay].isinRoom = 77;
}
} else {
std::cout<<"If two, condition B\n";
for (bx = fin->cent.x; bx <= start->cent.x; bx++)
{
layout[bx][by].blocks = false;
layout[bx][by].isinRoom = 77;
}
}
std::cout<<"Connection complete.\n";
i++;
}
}
Room Carving Function:
void Map::digRect(Rect room)
{
int x, y;
for (x = room.uL.x; x < room.lR.x; x++)
{
for (y = room.uL.y; y < room.lR.y; y++)
{
this->layout[x][y].blocks = false;
this->layout[x][y].isinRoom = room.idNum;
}
}
std::cout<<"Dug room "< }
And the main function that puts it all together:
void Map::genRect(int numRoom, int maxSize)
{
int i;
int posx, posy, possize;
int roomsMade = 0;
int collisions = 0;
Rect* posroom;
//screensections;
Rect scrSec1(1,1,width/2, height/2 - 1);
scrSect.push_back(scrSec1);
Rect scrSec2(width/2, 1, width/2 - 1, height/2 - 1);
scrSect.push_back(scrSec2);

Rect scrSec3(1, height/2, width/2, height/2);
scrSect.push_back(scrSec3);

Rect scrSec4(width/2, height/2, width/2 - 1, height/2 - 1);
scrSect.push_back(scrSec4);


std::cout<<"Screen partition complete.\n";
while (roomsMade < numRoom)
{
possize = getrand(5, maxSize);
switch (getrand(1,4))
{
case 1:
posx = getrand(scrSect[0].uL.x + 1, scrSect[0].lR.x - (possize + 1));
posy = getrand(scrSect[0].uL.y + 1, scrSect[0].lR.y - (possize + 1));
std::cout<<"Room: "< break;
case 2:
posx = getrand(scrSect[1].uL.x + 1, scrSect[1].lR.x - (possize + 1));
posy = getrand(scrSect[1].uL.y + 1, scrSect[1].lR.y - (possize + 1));
std::cout<<"Room: "< break;
case 3:
posx = getrand(scrSect[2].uL.x + 1, scrSect[2].lR.x - (possize + 1));
posy = getrand(scrSect[2].uL.y + 1, scrSect[2].lR.y - (possize + 1));
std::cout<<"Room: "< break;
case 4:
posx = getrand(scrSect[3].uL.x + 1, scrSect[3].lR.x - (possize + 1));
posy = getrand(scrSect[3].uL.y + 1, scrSect[3].lR.y - (possize + 1));
std::cout<<"Room: "< break;
}
std::cout<<"Room: "< posroom = new Rect(posx, posy, possize + (possize/2) , possize);
posroom->idNum = roomsMade;
std::cout<<"Room: "< posroom->cent.x = (posroom->uL.x + posroom->lR.x) / 2;
posroom->cent.y = (posroom->uL.y + posroom->lR.y) / 2;
std::cout<<"Room: "<cent.x<<"/"<cent.y<<"\n";
if (roomsMade > 0)
{
for (auto R = rooms.begin() ; R != rooms.end(); R++)
{ if (posroom->collision(*R)) { collisions++; } }
if (collisions == 0)
{
digRect(*posroom);
rooms.push_back(*posroom);
roomsMade++;
collisions = 0;
std::cout<<"Room "<idNum<<"made. x/y: "<uL.x<<"/"<uL.y<<" x2/y2:"<lR.x<<"/"<lR.y< } else {
std::cout< collisions = 0;
}
} else {
rooms.push_back(*posroom);
roomsMade++;
}
if (roomsMade == 2)
{
spy = rooms.at(0).cent.y;
spx = rooms.at(0).cent.x;
}
}

if (getrand(1,100) > 50)
{
connectRooms(rooms);
} else {
connectRooms2(rooms);
}
outline();
}

Yeah, damn, thats a doozy, and for a dungeon generating algorithim thats actually pretty straight foward
and compact compared to alot of the ones ive looked at. As promised, here is the extre clean up function.
like i said it works fine without this, but this makes it look MUCH nicer.



Optional dungeon cleaning function;
void Map::outline()
{
int x, y;
for (x = 0; x < width; x++)
{
for (y = 0; y < height; y++)
{
if (layout[x][y].blocks == false && layout[x+1][y].blocks == true)
{
layout[x+1][y].border = true;
}
if (layout[x][y].blocks == false && layout[x][y+1].blocks == true)
{
layout[x][y+1].border = true;
}
if (layout[x][y].blocks == false && layout[x-1][y].blocks == true)
{
layout[x-1][y].border = true;
}
if (layout[x][y].blocks == false && layout[x][y-1].blocks == true)
{
layout[x][y-1].border = true;
}
}
}
}


For the complete Source code of the project up to this point, click here
all source code is released under the MIT free software license.
Well, time to make more coffee, enjoy your dungeon.




Part 4: FOV


FOV, or Field of Vision is a pretty neat trick thats easy to implement, visualy cool, and gives another layer
of depth to your rogue like. The fact that it can be implemented with relativly few lines of code, and is computationaly
inexpensive(we can debate how costly sqrt() is on a later date) is probably why it found its way in to so many
early games as to become a common trope. Basically what we're going to do is turn the lights off, and thankfully
'@' was smart enough to bring a candle with him that allows him to see in a certain radial distance around his/herself.
So, what do we need in order to implement this in our game?

  1. We need a way to tell the distance between '@' and the tiles that surround him
    to know if they will be visible or not
  2. We need to know if we are in FOV mode or not

That's it, like i said, not much to implement. so lets take care of first task, knowing if a tile is
withing a certain distance of '@'. To do this, were going to add another function to our Map class
lets call that function isInFov(), for this function to work it needs to have the x and y position of '@'
and the x and y position of the tile in question. in dungeon.h add the following to the definiton of the
map class:


FOV additions to dungeon.h
bool fov = false;
double isInFov(int x, int y, int px, int py);

And now well go over to dungeon.cpp and the code for the isInFov() function:

dungeon.cpp/isInFov()
double Map::isinFov(int x, int y, int px, int py)
{
double xDis = (px - x) * (px - x);
double yDis = (py - y) * (py - y);
return sqrt((xDis+yDis)*1.0);
}

Remember all those kids in high school that said math is useless? ask them how to tell the distance
between two points on a 2d axis. Ahem, moving on. So now that we have our distance function to taken
care of, we just need to make a couple of tweeks to our drawAll function in main.cpp. Mainly, we need
to tell if fov mode is on or not, which is why added the bool fov to dungeon.h, and if it is on, draw
only the tiles that are within an allowed distance from '@'. lets make those changes now:

main.cpp/drawAll()
void drawAll(Map dungeon, Entity player)
{
double z;
int x, y;
terminal_layer(1);
for (y = 5; y < 40; y++) {
    for (x = 0; x < 70; x++) {
      if (dungeon.fov == false) {
        if (dungeon.layout[x][y].blocking == false)
        {
         terminal_color("darker red");
         terminal_print(x , y, ".");
      }
      if (dungeon.layout[x][y].borders == true) {
         dungeon.layout[x][y].blocking = true;
         terminal_color("dark grey");
         terminal_print(x, y, "#");
      }
      } else {
       z = dungeon.isinFov(x, y, player.px, player.py);
       if (z <= 4) {
         terminal_color("#E0D937");
       } else {
         terminal_color("black");
       }
      if (dungeon.layout[x][y].blocking == false)
      {
       terminal_print(x, y, ".");
      }
      if (dungeon.layout[x][y].borders == true)
      {
       dungeon.layout[x][y].blocking = true;
       terminal_print(x, y, "#");
      }
}
}
}

terminal_layer(2);
terminal_color("purple");
terminal_print(player.px, player.py, player.ch);
terminal_refresh();
}


Lastly, what i like to do is add a toggle to easily turn FOV on/off while testing/debugging
so in the gameLoop() i add a key event. so lets say we use the tab key, TK_TAB, as the toggle:

FOV toggle
case TK_TAB:
      if (dungeon.fov == false) { dungeon.fov = true; }
      else if (dungeon.fov == true) {dungeon.fov = false;}
      break;

So there's our FOV, like i said, pretty easy to implement, and a pretty cool visual.


Part 5: Theres something out there

Allright, things are really coming together now, we have our character '@', we have the entity class that we
can use to make other characters, we have the map so we can sprinkle these other characters through out, and
we can even turn the lights out and see how '@' handles being in the dark. So why dont we put some characters
on the map for ol' '@' to fight with.
We're going to have two types of enemies to start with, lets call them goblins and vampires, cause why not.
Before we throw '@' into a dungeon with a bunch of bad guys, lets at least give him the benefit of knowing what
direction he's facing. To do this we're going to add an enum, so make the following changes:

in dungeon.h add:
enum Direction {
   North,
   South,
   East,
   West
};
To the Entity class, add the line:
Direction dir;
In gameLoop() make the following changes:
case TK_UP:
   player.move(dungeon, 0, -1); player.dir = North;
   break;
case TK_DOWN:
   player.move(dungeon, 0, 1); player.dir = South;
   break;
case TK_RIGHT:
   player.move(dungeon, 1, 0); player.dir = East;
   break;
case TK_LEFT:
   player.move(dungeon, -1, 0); player.dir = West;
   break;

With that in place, we now know which direction '@' is facing, which will come in handy when its time to faceoff
with some bad guys. But before '@' can fight anybody, we have to put some badguys out there in the first place.
Since there is going to be quite a few bad guys, and theyll be coming and going, lets store them in a vector for
ease of management, this way we dont have to hardcoded each individual character. We're going to need a function for
placing the characters on the map, so the map class is a good place to put the spawnMonsters() function. so lets
do the following:


Adding vector to dungeon.h
std::vector<Entity> badGuys;
dungeon.h spawnMonsters()
void Map::spawnMonsters(int numBads, std::vector rooms) {
   int i, randRoom, x, y;
   Entity* badGuy;
   int numToSpawn = getrand(0, numBads);
   for (i = 0; i < numToSpawn; i++)
   {
      randRoom = getrand(1, rooms.size()-1);
      if (rooms[randRoom].numEnts <= 2)
      {
      x = getrand(rooms[randRoom].x1, rooms[randRoom].x2);
      y = getrand(rooms[randRoom].y1, rooms[randRoom].y2);
      if (layout[x][y].populated == false && layout[x][y].blocking == false) {
      if (getrand(0, 100) < 65) {
      badGuy = new Entity(x,y,'G', color_from_name("magenta"));
      } else {
      badGuy = new Entity(x, y, 'V', color_from_name("flame"));
      }
      badGuys.push_back((*badGuy));
      rooms[randRoom].numEnts++;
      layout[x][y].populated = true;
      layout[x][y].blocking = true;
      }
   }
   }
}

You may notice that we are checking the conditin of the tiles for a property called "populated"
We add populated to the tile class so we know if an entity, or an soon to come, an item, is on the tile
in the tile struct just add the line:


bool populated=false;

Add a call to spawnMonsters() at the end of your makeRooms() function to add bad guys to the rooms
as they are being created, dont forget to pass the number of monsters and the badGuys vector as arguments
If you go ahead and compile/run our game, you'll now see that there are now other Entitys besides '@' sprinkled throughout the
dungeon. Or, you may be having trouble compiling because at this stage in development, as our game and classes
become more complex, we start needing to access classes from other classes, which are in seperate .h files
that depend on eachother. For certain this is now the case with dungeon.h and entity.h. to make oure lives easier
as our project grows in complexity, what i do is put all the #include statements into one header(.h), though this
still doesnt solve the issue of both entity.h and dungeon.h needing to be before the other in order for
everything to be defined as we need. We are certainly not the first to have this issue whilst coding anything
but the most basic programs, and C++ has the answer to our problem in the form of the #indef flag. So lets make a new
header file to hold all of our #include statments, i called mine master.h:

master.h
#include #include
#include
#include "BearLibTerminal.h"
#include "helpers.h"
#ifndef map_h
#define map_h
class Map;
class Entity;
#endif
#include "ent.h"
#include "map.h"
#include "map.cpp"
#include "ent.cpp"

Now at the top of all your other .cpp/.h files replace all your other #include statements with simply
#include "master.h" and your code should compile and run as needed.

Now that we have our headers sorted out and our game compiling properly again. its time to get back to coding because now that we have everything in place
to be able to kick a badguy, lets add the code to actually kick 'em! pop open ent.h and ent.cpp and in the definition of your Entity class add the following decleration for a kick function, and implement it in ent.cpp:

ent.h additions
void kick(Map map);
ent.cpp additions
void ent::kick(Map map)
{
Point enemy;
int x=this->pos.x;
int y=this->pos.y;
switch (this->dir) {
case North:
if (map.layout[x][y-1].populated == true)
{
enemy=map.layout[x][y-1].pos;
}
break;
case South:
if (map.layout[x][y+1].populated == true)
{
enemy=map.layout[x][y+1].pos;
}
break;
case East:
if (map.layout[x+1][y].populated == true)
{
enemy=map.layout[x+1][y].pos;
}
break;
case West:
if (map.layout[x-1][y].populated == true)
{
enemy=map.layout[x-1][y].pos;
}
break;
default: break;
}
for (auto m : map.badGuys)
{
if (enemy == m.pos)
{
std::cout<<"The "< }
}

There's one more change we need to make in order to use this piece of code, something that will make our life much easier moving foward.
We're going to assign an == and != operator the Point struct to allow for direc comparison of coordinate locations, that way we dont have to compare x and y individually
every time we want to know if an object is at a given location. in dungeon.h, go to your Point struct and add the following:

adding operators to point
struct Point {
int x;
int y;
bool operator==(const Point& other) const {
return x == other.x && y ==other.y;
}
bool operator!=(const Point& other) const {
return x != other.x || y != other.y;
}
};

There ya go, now whener we want to know if something is at the same x and y as something else we can simply if (objectA.pos == objectB.pos) instead of
having to do if (objectA.pos.x == objectB.pos.x && objectA.pos.y == objectB.pos.y). If we wanted we could implement the full range of operators
(<,>,<=,=>,!=, ==) but we can get away with just these two serving our purposes for now. I should note that in the upcoming C++-20 this will be done
automaticall for us via the "space ship operator" (<==>).



Part 6: The graphical user interface



BearLibTerminal, while being a graphics library, is actually closer to a console library, and while it has all the features
that we need to make a roguelike, it is missing some of the things that makes the task a bit easer, such as console bliting,
predefined shape drawing, etc. But none of that has stopped us thus far, and it wont stop us now, what BearLib lacks in
functionality it more than makes up for in ease of implementation and overhead. One of the features it does have, that
we havent dabbled with yet, is its awesome layer system. Remember how i just said BearLib doesnt support libtcod style bliting?
I lied. BearLib allows for the user to stack multiple layers on top of eachother, by default all of the layers are transparent
to the layer below it, except for layer zero* where you can set the background color. By combining this with the terminal_clear_area()
function, we can build a pretty slick looking GUI, with status bars, stats, and a message log(no more std::cout in a seperate window!)

Before we get started lets take a look at the functions we're going to use from BearLib in order to make our GUI, some of these we've
used already, others we are seeing for the first time:

So, now that we have an idea of the functions we are working with, there is one more ace up our sleeve in terms of making
a slick looking GUI for our game: terminal_printf() supports Unicode! By combinig all of BearLib's formatting features with
Unicode characters, especially Box Drawing characters we will be able to make a pretty slick looking interface for our game.
(or at least something better than just some text on a screen).
So add gui.h and gui.cpp files to our project, and like usualy well start by outlining our class, but before we do that
lets stop and think about what we want our GUI to look like. What I demonstrate here, if i will partition the window into
3 sections:

  1. A top Banner area with a scrolling message log
  2. A side panel that will display statisics like player health(when we add it), item inventory(when we add one), etc.
  3. And of course, most of the window will be taken up by the playing field.

Here's a screen Shot of our tutorial game:

Before we even get to gui.h and gui.cpp theres a few changes were going to make to main.cpp before we start. Up till now we've been working
with an 80x40 terminal window which is the size of our map. This isnt going to work if our map takes up the whole window, and if its a choice
between a smaller playing area, or a bigger window, my votes on the bigger in window. So in main.cpp go to where we passed the window size
value to terminal_set() and make change it to this:

main.cpp changes
terminal_set("window: title="C++ tutorial" size=100x45");
and while we have that file make the corresponding changes to int width and int height:
int width = 100;
int height = 45;

So the first step we're going to take is to draw all the borders for the screen partitions. We're going to take advantage of being able
to set tha background color of layer 0, and our ability to draw on other layers on top of it without disturbing the background color.
There are two things we need to pay attention to at all times, the layere we are drawing on, and the order in which colors are laid down
so that we dont washover what we put down. So lets get started in gui.h

defining the gui class in gui.h
class Gui {
public:
int timeStart; //time the game started int scrW, scrH; //screen width and height
int mapW, mapH; //map width and height
int mapX, mapY; //x/y of upperleft corner of the map
void showTime(); //have a clock showing elapsed game time void drawGui(std::vector*); //what we will call to draw the gui
void banner(std::vector*); //this handles message log seperatly
void healthbar(ent player); //this will display the player health; void fade(int x, int y,int start[3], int fin[3], char *message); //a cool effect i put together for kicks :)
Gui(int, int, int, int, int, int); //our class constructor, the six int's corresponding to the six we declared above
};

With our class layed out we can open gui.cpp and get to work implementing our features, starting with the class constructor
and then we will work on drawGui() in stages before taking care of the banner and the message log, and then
the fade() function(which is optional, but fun). the class constructor has a curious looking bit of code at
the bottom of it, what it does is launch a sperate thread that calls the showTime() function every half a second
which calculates how much time has passed since the game started, this our game clock. Don't forget to #include <chorono> in master.h

Up until now, we havent paid attention to which layer we've been utilizing, but thats all about to change. Open thew new
gui.cpp file, and we'll get the constructor out of the way.

gui.cpp/Gui::Gui()
Gui::Gui(int sw, int sh, int mw, int mh, int mx, int my)
{
this->scrW = sw;
this->scrH = sh;
this->mapW = mw;
this->mapH = mh;
this->mapX = mx;
this->mapY = my;
//this will update the elapsed time timeStart = std::chrono::high_resolution_clock::now();
std::thread([&]
{ while (true) { std::this_thread::sleep_for(std::chrono::milliseconds(500));
showTime();
}}).detach();
}

And since we're on the topic of the game clock, lets implement the showTime() function:

gui.cpp/showTime()
void Gui::showTime()
{
std::chrono::duration elapsed = std::chrono::high_resolution_clock::now() - timeStart;
int hour = 0;
int min = 0;
int sec = 0;
if (elapsed.count() > 60)
{
min = elapsed.count() / 60;
sec = elapsed.count() - (60*min);
} else {
min = 0;
sec = elapsed.count();
}

terminal_layer(2);
terminal_print(mapW+3, 12, "Elapsed Time");
terminal_clear_area(mapW+3,13,10,1);
if (sec < 10) {
terminal_printf(mapW+3, 13, "%d:0%d", min, sec);
} else {
terminal_printf(mapW+3, 13, "%d:0%d", min, sec);
}
}

Now, the first part of drawGui() we're going to get out of the way is layoung down the dividing lines to
partition the screen into "framed" sections, for the outline and major partitions we'll be using background fill
to draw thick lines, for other features, well be outlining them using the unicode box drawing characters we talked
about earlier:

gui.cpp/drawGui()
void drawGui(std::vector gamelog)
{
int x, y;
terminal_layer(0);
for (x = 0; x < scrW; x++)
{
for (y = 0; y < scrH; y++)
terminal_bkcolor("black");
terminal_print(x, y, " ");
terminal_bkcolor("#cecece");
terminal_print(x, 0, " "); //top row, all grey
terminal_print(x, 6, " "); //grey row to delimit the banner area
terminal_print(x, scrH, " "); //bottom row all grey
terminal_print(0, y, " "); //left most column, all grey
terminal_print(mapW+1, y+6, " "); //first column to the right of the map, from bottom of banner, to bottom of terminal.
terminal_print(scrW-1, y, " "); //right most terminal column
}
}
terminal_color("red");
terminal_print(mapW+2, 7, box["uL"].c_str());
//Health bar box terminal_print(scrW-2, 7, box["uR"].c_str());
terminal_print(mapW+2, 8, box["side"].c_str());
terminal_print(scrW-2, 8, box["side"].c_str());
terminal_print(mapW+2, 9, box["side"].c_str());
terminal_print(scrW-2, 9, box["side"].c_str());
terminal_print(mapW+2, 10, box["lL"].c_str());
terminal_print(scrW-2, 10, box["lR"].c_str());
terminal_color("red");
terminal_print(mapW+2, 11, box["uL"].c_str());
//Health bar box terminal_print(scrW-2, 11, box["uR"].c_str());
terminal_print(mapW+2, 12, box["side"].c_str());
terminal_print(scrW-2, 12, box["side"].c_str());
terminal_print(mapW+2, 13, box["side"].c_str());
terminal_print(scrW-2, 13, box["side"].c_str());
terminal_print(mapW+2, 14, box["lL"].c_str());
terminal_print(scrW-2, 14, box["lR"].c_str());
for (x = mapW+3; x < scrW-2; x++) {
terminal_print(x, 7, box["top"].c_str());
terminal_print(x, 10, box["top"].c_str());
terminal_print(x, 11, box["top"].c_str());
terminal_print(x, 14, box["top"].c_str());
}
//with the outlines drawn, we can now fill with content.
this->banner(gamelog); //have the game log messages scrolling
this->sidebar(player); //player health bar
}

So now that we have the screen partitioned out the way we want, we're ready to setup the scrolling message log
What this function will do, is say when we kick a monster, instead of printing "the monster was kicked" to the console
where we wouldnt be able to see it during game play.

gui.cpp/banner()
void Gui::banner(std::vector* gamelog)
{
char *title = "C++ & BearLibTerminal Roguelike Tutorial!";
int i;
int x, y;
int sentSize;
int start[3] = {10,55,200};//{128,128,128};
int fin[3] = {255,0,0};//{0,255,255};
fade(scrW/2 - strlen(title)/2, 0, start, fin, title);
std::vector::iterator it;
std::string messagebuff;
int m = 4; int l = 0;
if (gamelog->size() >= 1){
for (auto i : *gamelog)
{
messagebuff = i;
if (m - l == 1)
{ l = 0; }
terminal_printf(4, m - l, "%s", messagebuff.c_str());
sentSize = gamelog->size();
l++;
}
}
}

And thats our scrolling message log/banner at the top of the screen So now we have the messages scrolling. our, elapsed time
is showing on the side bar, lets add A player health bar to the sidebar so we can track health/damage during fights, etc. But wait
Player health? Yep, something we havent yet added, so open ent.h, and ent.cpp and make the following additions:

ent.h/ class ent addition class ent {
....
....
double health;
...
...
}


ent.cpp/ ent::ent() addition
ent::ent(......)
{
...
...
...
this->health = 100.00;
...
}

thats more like it, now that our player has a health value, we can add a spiffy visual to track it:

guid.cpp/healthBar()
void Gui::healthBar(ent* player)
{
double x, y;
int cent = (((this->scrW -2) + (this->mapW+3)) / 2) - 5;
double health_unit = 100 / ((this->scrW - 2) - (this->mapW+3));
double health_amt = (player->health) / health_unit;
int health_bar = (mapW + 3) + health_amt;
terminal_layer(1);
terminal_color("white");
terminal_print(mapW+3, 8, "Health");
terminal_printf(cent, 9, "%f\%", player->health);
terminal_layer(0);
terminal_bkcolor("red");
for (x = this->mapW + 3; x < this->scrW - 2; x++)
{
terminal_print(x, 9, " ");
}
terminal_bkcolor("green");
for (x = this->mapW+3; x < health_bar - 1; x++)
{
terminal_print(x, 9, " ");
}
}

And there we have it, the basics of nicely laid out functioning gui. Game info scrolling on the screen in realtime,
Health info about our character, elapsed time in game, and whatever else you choose to display as you see fit.
When we add items to be picked up/used at a later stage, we'll come back to this section and add an inventory list.

this next part i made just playing around one day
and i thought it made a cool effect for like, a game title at the top, it fades the color of a string of text starting from
one color, fading across the text into another color. You can use this just like, and infact in lieu of, terminal_print
the only difference being is you need to also provide (2) three int arrays which hold the RGB values of the starting color
and finishing color. other than that, it takes an int x, int y, and a string just like terminal_print()

gui.cpp/fade_print()
void Gui::print_fade(int x,int y,int start[3],int fin[3],char *message)
{
int i;
int stepping = strlen(message);
int distance[3] = {(fin[0] - start[0])/stepping,
(fin[1] - start[1])/stepping,
(fin[2] - start[3])/stepping,};
int color[3] = {start[0], start[1], start[2]};
terminal_layer(3);
for (i = 0; i < stepping; i++)
{
color[0] = color[0] + distance[0];
color[1] = color[1] + distance[1];
color[2] = color[2] + distance[2];
terminal_color(color_from_argb(255, color[0], color[1], color[2]));
terminal_print(x+i, y, message + i);
}
}
Example usage of fade_print()
char *title="C++ & BearLibTerminal Roguelike Tutorial!";
int start[3] = {128,128,128};
//starting color represented as {R,G,B} int fin[3] = {0,255,255};
//finishing color represented as {R,G,B} fade_print(scrW/2 - strlen(title)/2, 1, start, fin, title);

Example output:

Thats our game, with FOV mode on, kicking a goblin showing the result of the kick in our message log
The title up top is the output of our fade_print() function.

Now that we have our Gui Code in prepared and our message log functional lets incorporate it into the project
so that when our game loads up, the gui is in place, and set it up so that all non-debugging output gets routed
to the message log to be displayed at the top of the screen. Note: the message log is 94 charcters wide,and two
rows tall. We will be displaying the last two messages recieved at any given time, except obiously, when we dont have
to get any. We do have to make a few fundemental changes to our main game loop, because up until this time our code
has been running in whats called blocking mode, the main condition of our loop has been (k = terminal_read() != TK_Q)
Which means the loop will continue so long as you dont press 'Q'. the problem with the way its being handled at
the moment, which you may have noticed if youve compiled and tested the clock function on the gui, is that this condition
halts all execution as its waiting for input from the terminal. we're going to change that by doing two things:

  1. We're going to change the condition for controlling the loop
  2. We're going to use the BearLibTerminal function terminal_has_input() function, which returns a bool in regards to if a keyboard or mouse
    event has taken place, if and only if this happens does the code go to the terminal_read() function which is what causes the blocking

First We're going to address the blocking issue, and then we'll incorporate the rest of the gui into the code
First things first though, open up main.cpp and add the very top of int main(), above the
first call to terminal_open(), add the following:

adding gui code to main.cpp/int main()
std::vector gamelog;
Gui gui(6, 1, 80, 40, 100, 45);
bool gameIsRunning = true;
Changes to the main.cpp/gameloop:
while(gameIsRunning)
{
if (terminal_has_input()) { k = terminal_read(); handle_keys(k, &player, &map, &gamelog);
} terminal_clear();
gui.drawGui(&gamelog);
drawAll(map, player);
player.render();
terminal_refresh();
}
Changes to main.cpp/handle_keys
void handle_keys(int k, ent* player, Map* map, std::vector* gamelog)
{
....
....
....
case TK_K:
player->kick(*map, gamelog);
break
; case TK_Q:
terminal_close();
exit(0);
....
....
}

Now when you compile and run the game, you'll see the elapsed time ticking away in the gui
You'll also notice that every call to drawGui() has &gamelog as a parameter.
thats because we declared the std::vector gamelog in int main(), and so in
drawGui, were using a pointer to the vector. I did this way because the reference
to gamelog is going to be passed around ALOT so its far better to be shuffling around
the memory address of the vector than the actual vector itsself. For example, the only thing
drawGui is doing with the reference to gamelog is passing it to gui.banner(); where banner
will access it and display its contents as we programmed it to do earlier. You can see in
the gameloop, we pass it to handle_keys() and from handle_keys() its passed to the kick() function
in our player implementation of the entity class. Like i said, its passed around like a bad penny.
Lets open up ent.cpp and see what we're doing with it in Entity::kick()

ent.cpp / Entity::kick()
void ent::kick(Map map, std::vector* gamelog)
{
Point enemy;
std::string msg;
int x=this->pos.x;
int y=this->pos.y;
....
....
....
for (auto m : map.badGuys)
{
if (enemy == m.pos)
{
msg = "The " + m.name + " howls in rage at your attack.";
gamelog->push_back(msg);
}
}

As you can see, now when an action takes place that we previously would have used std::cout to print
msg on the console, now its pushed into the gamelog vector, where on the next run through the gameloop
the vector will be passed to Gui::drawGui(), Gui::drawGui() will pass the vector to Gui::banner(), and
from there the the two newest entries in the gamelog vector will be displayed, as you saw in the screenshot
demonstration above. good ol' std::vector gamelog, she sure does get around!



Part 7: Fighting & Damage


    All of the pieces are taking their place, and everything we implement build's on what came before So, now that
we've introduced a health bar, and action messages, why not implement some action in the form of fighting? There's alot of
different ways that this can be implemented, from incredibly complex Artificial intelligence engines, to BSP tree's, round robin if statement code
nested switch statements that will make your eye balls bleed, etc. Seeing as we're aiming at just having a game thats playable, were not going to pull out all the stops and
start coding machine learning ninjas and such, were going to keep it simple, but still keep it dynamic enough that its fun.
This isnt a particularly, "code heavy" section, but what we do hear definitly add's to the fun factor tremendously. And once
you see the gist of how it works, its very easy to expand. Why just kick? how about kicks and punches? Your in FOV, so your character
has a torch, can we clobber the bad guy with it? you reall have space to get creative here, so go nuts!

    If you recall from earlier, when we implemented the ability to kick a monster, all we did in the case of that event, was determine if our
kick landed or not, and update the game log accordingly. We're going to build from that now, so that if our kick lands, the monster
has the opportunity to respond in turn. To keep it dynamic, we'll add in a probabbility that only 80% of the monsters counter attacks are succesful.
Not only does this mean, their hit missed, but it creates a more natural random turn taking order, in so much that
its not just a i kick, you kick, i kick, you kick type fight.

    To add to that, were algo going to have the amount of damage given and recieved be generated within a range, so not every kick is -1hp,
this also helps keep the fight dynamic because its not a simple "he who kicks first gets the enemys health to zero first", its more
"fair" of a fight. With this in mind, lets get started.

   The first thing we're going to do is open a file we've been neglecting a little bit, the helpers.h file and were going to add
another version of the getrand() function, but this time were going to have it generate float's. This is for coming up with damage dealt to our
opponents and player. For now, we're going to assume that the monsters and player have a reasonably similar strength, and that their strength stays
constant over time, later we'll add the ability to grow stronger, take super strength potions etc, that will change the way we calculate
damage. But for now, lets just get something working and implemented before we start complicating it. (write that down.)

helpers.h getrandfloat() function
double getrandfloat(double min, double max)
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution distrib(min, max);
return distrib(gen);
}

If you ever want to debate with somebody about the difference between coding C++ in C++ vs. Coding C in C++, tell them to observe the difference
between using the STL <random> functions vs srand()/rand()/time() on something thats dependent on decent randomness (like say.. procedural dungeon generation...)
You can drive a screw with a hammer, but it works better with a drill.


Open up ent.cpp and find your way to the kick() function, as thats where the majority of the changes to our code will be taking place.
Right now, the pertinent bits of our kick function look like this:

A kick with no punch
void ent::kick(Map map, std::vector* gamelog)
{
....
....
....
for (auto m : map.badGuys)
{
if (enemy == m.pos)
{
msg = "The " + m.name + " howls in rage at your attack.";
gamelog->push_back(msg);
}
}

So thats what were working with so far, and believe it or not, that actually puts at 50% of our goal of having monsters that fight back when you
hit them. Monsters that attack unprovoked arent as simple but still not earth shatteringly complex, more on that to come, but for now lets break down
what we already have to empower us with what we need to move foward. As we discussed in section five:

  1. this function is evoked when the user is standing next to a monster and hits the 'k' key
  2. our map class is passed as a parameter, so that our function can look up the values for tiles
  3. if it determines that the tile we are facing is populated....
  4. it determines WHO is populating that tile
  5. anounces the monster standing on the tile, has been kicked

Compounding on this idea:

  1. first we are going to incorporate the health property that we added to the entity class in part 6.
  2. now, we'll create an 80% chance that when we kick the monster, the monster successfully kicks us back.
  3. if the odds are in the monsters favor, we calculate another damage value, but this time we subtract from the players health
  4. if they missed, they are immediatly open to attack again

Mind you that between all these steps we are updating the gamelog so the player has a story to follow, we discussed updating and present the
gane log in the last section if you need to go back for a refresher. So Lets take a look at our kick function following the steps we laid out
above:

A kick that kicks back...
void ent::kick(Map map, std::vector* gamelog)
{
....
....
....
for (auto m : map.badGuys)
{
if (enemy == m.pos)
{
double dmg_done = truncf(getrandfloat(0.75, 3.85) * 10)/10;
m.health -= dmg_done ;
msg = "The " + m.name + " howls in rage at your attack." + m.name + " recieved " + std::to_string(dmg_done) + " damage";
gamelog->push_back(msg);
if (getrand(1, 100) > 20)
{
dmg_done = truncf(getrandfloat(0.75, 3.85) * 10)/10;
msg = "The " + m.name + " returns a savage blow. You recieve " + std::to_string(dmg_done) + " damage";
gamelog->push_back(msg);
this->health -= (dmg_done * 10)/10;
} else {
msg = "The " + m.name + " trys to retaliate but misses! Now is your chance!";
gamelog->push_back(msg);
}
}
}

Like i said, pretty rudimentary, an uncomplicated way of having monsters fight back in a dynamic way thats not just "i kick, you kick, repeat".
Theres two more things i'm going to discuss, and ill provide a break down for them so you have an idea on how to implement them. The first possible
(and rather logical addition) would be to incorporate monster, and conversly, player death; the execution path for that would look like:

  1. calculate the amount of damage to be dealt
  2. if monster health - damage <= 0;
  3. If the monster health - damage > 0 (but NOT >= 0, zero is dead, and dead is dead. even in video games.)

So thats one way we could incorporate that into the fight. Another think we could do spice the fight up a little, is instead of the
monster either kicking back or missing, we could add other types of attacks the monsted can launch at us, and if we want to really
go in depth, we could have certain types of monsters able to have special attacks, that are above the basic attacks that all monsters have.
First lets look at simply varying up the attacks
We'll pick up At the stage of the fight logic where The monster is retaliating but we havent yet chosen the damage amount
Instead of straight away calculating the damage, we will:

  1. generate a random number between 1 and n(attacks)

Theres really alot you can do with this simple but effective fighting function, go nuts!

Now, it wouldn't be very fair, nor would it be much fun (and someone told me games are supposed to be fun) if we just had a map full of monsters that just
stood there and did nothing, waiting to get kicked. So how can we make the monsters bring the fight to us? Roguelikes are traditionally a turn-based game.
This means i take a turn, the opponent takes a turn. simple concept, and one that up to now we have ignored. So its time to give the monsters a turn, and after
one monster takes a turn, the next one does, until it gets back to our turn. We're also going to need a way to determine what the NPC will do when its their turn
So we're going to create a VERY simple ai class, with its own ai.h/ai.cpp. For implementing the turn sequence, we will use a bool to determine whether its our
turn or the monsters turn. To do this we need to make a change to our game loop. At the beginning of the game loop, add the turn bool:

bool myTurn = true;

and in the actual loop is self make the following changes:

if (terminal_has_input()) {
myTurn = true;
k = terminal_read();
handle_keys(k, player, &map, &gui);
myTurn = false; }

if (myTurn == false)
{
skyNet.moveMonsters(&map, &player->pos);
myTurn = true;
}

As you can see were calling a moveMonsters() function from a class called skyNet. skyNet is the name i used for my ai class. mainly because i have a cheesey
sense of humor. For our ai(skyNet!!) header/cpp We'll use the following:

Ai.h
class ai { public: void moveMonsters(Map*, Point*); ai(); };

ai.cpp
ai::ai()
{

}

void ai::moveMonsters(Map* map, Point* playerPos)
{
int eX, eY;
for (auto m : map->badGuys)
{
if (m->id != 420666)
{
if (m->pos.x < playerPos->x) { eX = 1; }
if (m->pos.x > playerPos->x) { eX = -1; }
if (m->pos.y < playerPos->y) { eY = 1; }
if (m->pos.y > playerPos->y) { eY = -1; }
if (m->pos.x == playerPos->x) { eX = 0; }
if (m->pos.y == playerPos->y) { eY = 0; }
m->move(map, eX,eY);
}
}

}


compile and run the game, and watch as every time you make a move, the monsters come running towards you. Later on We'll give them a little more "pizzaz", and some better move fighting moves.


A short Note on using tilesheets & fonts.


Theres several schools of thought when it comes to game presentation for rogue like games.
Notably, the original Rogue, and its bastard cousing Nethack were purely text based, or i should say
ASCII based. This was in part due to the limits of technology at the time, and as games moved from the domain
of timeshared UNIX mainframes to ever increasingly powerful personal computers in the home, people began
adding graphics to games, rudimentary at first, to the mind bowing 3d graphics we see today.

BearLibTerminal has the ability to use tilesets and alternative fonts. For copyright purposes
i'm going to use the sample tileset and font that comes in the BearLibTerminal sampleomni program.
the tile set is this one:


Tiles.png

We'll also be using the VeraMoBd.ttf font file. The BearLibTerminal functions we'll be using are:

The way BearLibTerminal handles tilesheets, is you load the bitmap with a an address pointing to the first tile
By supplying the dimensions and spacing of the tile, each one is loaded in sequential order correspongding to address.
0xE000 and 0xF000 are the recommended addresses. Alternativly, if you have solitary tiles you wish to load one at a time, simply
chooses and address, and give the size and it will load that tile to that address. I'll demonstrate both methods below.

We're going to load Tiles.png to 0xE100. each tile is 32x32, and we'll use the default spacing. You can change the color
of the tile by using the usual terminal_color() function. infact, we'll be using the same image with different colors to make
the brick ground, with a stone border. in main.cpp, make the following additions right below where you did the terminal_set()
to set the window size:

main.cpp/int main() additions
int main(int argc, char* argv[])
{
...
...
  terminal_set("window: size=140x45");
  terminal_setf("font: VeraMoBd.ttf, size=8x16;");
  terminal_set("0xE100: Tiles.png, size=12x16, spacing=1x1;");
....
....
}

Now we just change the two terminal_print() statements we used for the ground and border in the drawAll() function
to the corresponding terminal_put() call's using the integer representation of of the tiles we want to use

main.cpp/drawAll() changes
void drawAll(....)
{
.....
.....
if (map->layout[x][y].blocks == false)
{
terminal_layer(0);
terminal_color("#8b4513");
terminal_put(sx, sy, 57643);
......
.......
if (map->layout[x][y].border == true)
{
terminal_layer(1);
terminal_color("#c2c2c2");
terminal_put(sx,sy,57643);
}
....
....
}


those simple changes, change our map to look like this:

Not bad a for a few lines of code, definitly ups the visual factor 1000% in my opinion. I'm not gonna go crazy
with tiles for the sake of keeping this tutorial manageable, but use your creativity, use images for monsters,
for your player, for items.... speaking of items, back to our regularly scheduled program.


Part 8: Adding Items & inventory Management.

Theres ALOT of different ways to go about implementing items in to a game, and one of the MAJOR decisions, is
what kind of data structure you're going to use to represent the item. For the purposes of designing our data structure
lets talk about the purpose of the items. there isnt much point in having an item that does nothing. But what could our Item do?
In part 7 we introduced the concepts of character health, and the ability to alter that health with damage done by kicking.
So those two things along, give us the ability to created three different kind of items:

So, our, combined with the name of our item, thats three properties that our data structure should have. Since we used floats for player health and damage, it makes sense that the +health and +power should also be floats. So as it stands our data structure looks like this:

Not much, but its enough for us to get started with and start designing our item system. If this was the only things our items were ever going to be
we could use std::tuple and stuff two random floats and string together, and put it all in a vector and call it a day. say... some thing like this:


std::vector<std::tuple<std::string, double, double>> items

and interesting idea, and if we were aiming for the most simple of games, id say not a bad idea either.
but what about something a little more customizable, a little more organizable. something thats easier to manipulate
something like this:

struct Item {
   std::string itemName;
   double powerInc;
   double healthInc;
};

Ok, Now were getting somewhere. But i think we can still do better. What if we want the ability to do more with our Items?
what if we want our items to be able to inherit the properties of other items? what if we want to inherit properties from items?
How about:

class Item {
double healthInc;
double powerInc;
protected:
std::string name;
public:
void item::useItem();
void item::tradeItem();
void item::stickItemUpPlayersBum();
};

Ok, maybe we don't need that last method, but you get the idea. What makes this so superior? and whats the difference
between a struct and a class anyway? Both fair questions, and believe it or not related. Alot of people have argued that
C++ has no need for the struct type since it has class, and that struct is only thrown in because of its descendence from C.
Thats a narrow view point, but the decision to give structs the ability to have member functions in C++ (something that C structs lack)
Didnt not help the argument any. Personally, i believe structs are best used as they were intended to be used in C, for cohesivley grouping together data of different types that are related, which is what was shown above, and the classes should be used when we intend
to have modifiers (member functions) as we're doing now.

Now that we've figured out how were going to shape our item lets add an item.h and an item.cpp file to our project:

item.h
class Item {
private:
double heals;
double powers
protected:
std::string name
public:
Point pos;
void useItem(ent* player);
void render();
Item(int x, int y, std::string n, double h, double p);
Item();
}

Item.cpp
Item::Item()
{
//If your wondering, shuts the compiler up about default constructors
//when we allocate classes dynamically
} Item::Item(int x, int y, std::string n, double h, double p)
{
this->name = n;
this->heals = h;
this->powers = p;
this->pos.x = x;
this->pos.y = y;
}

void Item::useItem(ent* player)
{
player->health += this->heals;
player->strength += this->powers;
this->heals = 0;
this->powers = 0;
//you never know... }

void Item::render()
{
terminal_layer(3);
terminal_color("red");
terminal_print(this->pos.x, this->pos.y, "%");
};

You may have notice in that last line of code, that we'll be representing items on our map with a "%" symbol
because they raise the health and power %. (get it?) So now that we have makings of an item we need a way to create them
and place them around our map, We've already done something similar with our spawnMonsters() function, we could, if we wanted
picky back on that function, rename it spawnMonstersAndItems() etc, etc, but that spits in the face of our object organizational
efforts. So we'll be adding another function to the map class called placeItems(), and an std::list (you can use a vector if you want)
To hold them:

map.h/ map class placeItems definition
class Map {
...
....
std::list<Item*> items;
void placeItems(int numItems);
...
}
map.cpp/placeItems
void placeItems(int numItems)
{
int sect;
int rit;
float rp, rh;
int itemsMade = 0;
Point randPos;
Items* item;

while (itemsMade < numItems)
{
rp = getrand(0.10, 2.25);
rh = getrandfloat(4.35, 9.33);
rit = getrand(0,22);
sect = getrand(0, 3);
if (scrSect[sect].numItems < numItems)
{
randPos.x = getrand(this->scrSect.at(sect).uL.x + 1, this->scrSect.at(sect).lR.x - 8);
randPos.y = getrand(this->scrSect.at(sect).uL.y + 1, this->scrSect.at(sect).lR.y - 8);
if (this->layout[randPos.x][randPos.y].blocks == false && this->layout[randPos.x][randPos.y].populated == false)
{
item = new Items(randPos.x,randPos.y, item->things.things[rit], rh, rp);
this->itemList.push_back(item);
this->layout[randPos.x][randPos.y].populated = true;
itemsMade++;
scrSect[sect].numItems++;
}
}
}
}

lets go over this function, because while its similar to the one we used for creating NPC's, this one differs in a few crucial ways, lets take a look.
sect, rp, and rh, are valuea to hold randomly generated +health, +power, and screen section(whoa, remember that list? never throw away data!). rit is used to generate a number
that cooresponds to an array index of an array of names i created to name Items, you can do the same, or skip that part and just name everything "item", i left that
part out because i didnt think i needed to explain how to creates a list of 25 Item names(if you need help with that, i'm assuming you stopped reading several sections ago).
So we have a loop to keep creating items untill we reach our total, then we generate the +health, +power, and +name of the item to be created, then we choose a section
of the map to place it in. If you recall from spawnMonsters() we did that on a room by room basis. this time we're distributing the items anywhere walkable in
each of the four quadrents of the map. This means that unlike monsters (for now) items can be placed in the corridoors that connect rooms. So, then we make sure there aren't too
many items already placed in this secton, and if that test passes, we generate a point in that section, we then test that point for two conditions: Is this point possible to get too? and
is there already an Item or a monster sitting at this position? If this test is passed also, we then allocate the item, add it to our item list, mark the spot on the map as occupied
so we dont place two items in the same spot, increment our counters, and repeat.

Ok, items made, check. items placed on the map, check. Now we have to give the player a way to pick the item up. Now we come to another design decision:
Do we want the player to have a choice in whether or not they must accept whatever item they find? and if they accept the item, must they use it immediatley, or can the player
carry it around with them to use as they see fit? The answer to those questions as it relates to this tutorial, are no, they dont have to keep an item they dont want, no, they dont
have to use it immediatley, they can save it for later. How do these design questions effect us? Well, we're going to need to add key strokes to our handle_keys() function, we're going to
add a vector (or list) to our ent class to carry the items, and lastly, we'll continue the GUI implementation for showing us what items our player has. First, were going to add the
ability to find out what an item is. We'll facilitate this by: when the players character is standing on a tile that is also containing an item, the player can learn what that item
is pressing the 'i' key. Because we will eventually be adding more possible keystroke commands than just 'i', were going to creat a second key handling
function that is strictly for dealing with items and interacting with the item menu we will be creating, but first we will add the 'i' command to the normal
handle_keys() function:

main.cpp/handle_keys()
void handle_keys(int k, ent* player, Map* map, Gui* gui)
{
switch (k)
{
...
...
case TK_I:
  player->handleItems(map, key);
  break;
...
...
}

As you can see we'll be handing control off to a function that we are adding to the ent class, and passing along the pointer to map(where the items are located)
and the GUI(as to present the information to the user). Since we're updating the ent class to add the handleItems() function, well also add the vector for holding
items the player decides to keep, and the functionality to keep the item. So in ent.h and ent.cpp we have to make the following additions:
ent.h additions
class ent {
...
...
std::vector<Item*> inventory;
void handleItems(Map* map, Gui* gui);
...
}
ent.cpp/handleItems()
void ent::handleItem(Map* map, Gui* gui)
{
std::string msg;
int k;
int cnt = 0;
int selection;
for (auto i : map->itemList)
{
if (this->pos == i->pos)
{
gui->gamelog.push_back("You found a " + i->name + "!\n");
gui->banner();
while (true) {
if (terminal_has_input()) {
k = terminal_read();
switch (k)
{
case TK_K:
this->inventory.push_back(i);
gui->gamelog.push_back(i->name + " was added to inventory");
map->itemList.erase(map->itemList.begin() + cnt);
return;
break;
default:
gui->gamelog.push_back("Eh, probably dont need it anyway..");
gui->banner();
return;
}
return;
}
gui->drawGui(this, map);
drawAll(map, this, 2);
terminal_refresh();
}
} else {
}
cnt++;
}
}

So, how this function works: when the player stands on a tile that has an item on it, they press the 'i' key which calls this function
the first thing this function does is loop through all of the items, to see if any of them are located where the player is standing. If not, nothing happens.
if there IS an item there, it adds a message to the game log that the player found XXX item which is displayed on the screen. It then waits for
furthur input from the keyboard, if the user walks away or presses any key besides 'K', the function exits and the player can go on their merry way
HOWEVER, if they player presses the 'k' (for keep) then the item is removed from the maps item list, and added to the players inventory. But thats not
where the story ends, because in our Gui class, we have a function whos purpose is displaying a list of items in the players inventory:

Gui.h & Gui.cpp
class Gui {
...
...
void showInventory(ent* player, Map* map, std::map<std::string,std::string> box);
...
}
gui.cpp
void Gui::showInventory(ent* player, Map* map, std::map box)
{
int i = 0;
int start[3] = {0,255,0};
int fin[3] = {255, 0, 0};
std::string msg = "Player Items";
fade(mapW+3,18,start,fin,msg);

terminal_color("red");
terminal_print(mapW+2, 17, box["uL"].c_str());
terminal_print(scrW-2, 17, box["uR"].c_str());
for (auto item : player->inventory)
{
terminal_printf(mapW+3, 19+i, "%d)%s %02f/%02f", i, item->name.c_str(), item->heals, item->powers);
terminal_print(mapW+2, 18+i, box["side"].c_str());
terminal_print(scrW-2, 18+i, box["side"].c_str());
i++;
}
terminal_print(mapW+2, 19+i, box["lL"].c_str());
terminal_print(scrW-2, 19+i, box["lR"].c_str());
terminal_print(mapW+2, 18+i, box["side"].c_str());
terminal_print(scrW-2, 18+i, box["side"].c_str());
for (int x = mapW+3; x < scrW-2; x++)
{
terminal_print(x, 19+i, box["top"].c_str());
}
}

It may look complicated, but 90% of that code is for drawing the outlining box of our list that grows and shrinks depending on
how many items the player has in their inventory.
The for (auto itme : player->inventory) loop is the meat and potatoes of the function.


(c) 2020 Max Goren - maxcodes.info
maxgoren@icloud.com
All source code is released under the MIT free software license and is considered free and opensource.