Roguelike Game in C++17

The entity and the map

Moving around in a blank world is not fun. Also, code at present has no concept of other things and creatures that could exist. So to fix that further we delve.

Goals

We have two main goals for this part.

  • Use an Entity class, to manage information like player's state and abilities.
  • Use an Map class to setup some obstacles.

The entity class

First thing we'll do is replace move_dir typedef, with a strongly-typed struct. position struct will have 2 members x and y. Anywhere we had move_dir gets replaced by position.

Next we create an Entity struct, this will describe our game entities. It has 3 member variables: position, character and color. It also has 2 member functions: move_by and draw_entity. move_by takes an offset position and updates entity's position. draw_entity takes a console object by reference and calls console's methods to draw itself. This part we'll change in next section as current setup is not ideal.

Now we change main function, replace player_x and player_y with player entity object, with same position values are before while also adding character and color to be used.

In the actions switch block, change how the player position is updated.

And further down, below the actions switch block, where drawing occurs, we change the two console method calls with one player.draw_entity call.

GitHub Repo

Refactoring for hygene

Before we go any further, we need to reorganize the code a bit. Everything is currently in the main.cpp. It's getting a bit crowded, and difficult to visualize the full flow. So will we break down our program in to multiple files.

  • main.cpp: start of the application remains here.
  • console.[cpp, hpp]: our "wrapped" tcod console objects.
  • game_entity.[cpp, hpp]: entity class lives here.

The concept here is, right now we are directly using tcod functions and objects. We want to clean up that usage a bit, make it more "safer". tcod's root console methods are exposed as static member functions. And one has to know the exact order to call some of them in. So in order to not memorize or constantly check the documentation we wrap those, as we use them, in console_root class.

console_root when initialized, will ensure that root console exists, and ensure that TCOD_quit will get called on exit. It will also expose methods which wrap functionality we use, e.g. toggle_fullscreen.

console_layer is another class that is in console.cpp|.hpp, this wraps tcod's offscreen buffer. It also has draw method, which will take an entity object and draw it out to contained offscreen buffer.

game_entity at this moment it only has 3 member variables and 1 method. Member variables describe where and how entity is to be drawn. Method allows owner of entity to move it by a specific offset position.

GitHub Repo

Beginning of a map

Refactoring done, time to move on to second goal for this section, creating a map/level structure. This will represent the movement area for the player. As well as, places player can collide with. Collidable area will be colored differently from regular ground area.

Each cell in our map will be represented by a tile data structure. We'll hold each tile in a game_map structure in a std::vector. game_map knows size of the movement area via width and height. is_blocked method will take a position arguement and return true if that location has a blocking tile.

We add 2 free functions generate_map and draw_map. You can probably guess what each of these functions does. Both will get called by main function. Before our main game loop, we call generate_map, and before drawing the player we call draw_map.

To prevent the player from going on top of blocking tile, we add check before player.move_by and call game_map.is_blocked with potential value of where player will be. If is_blocked return false, player can move to that location.

Part-2c.gif

GitHub Repo

Refactor part 2: the refactoring

Once again, code has gotten a bit hairy ಠ╭╮ಠ. So it's time to clean up again. We have a good idea of how the map is to be generated, and how collisions will work. It is now an exercise to clean up this working code into something that still works but is manageable. This is circle of programming.

This time around I am moving the code input handling code out, on top of moving tile, game_map and generating and drawing functions as well as pulling out actions functionality.

By the end of this set of refactoring activity, the project will have many files. We need to make sure that CMake is aware of them, which means adding each to target_sources in src\CMakeLists.txt. Over time, this list will get larger but not that much more larger.

Part-2d.gif

Links

GitHub Repo ╠═══
Home ╠═══
╣ Prev: Console and moving around ╠═══
╣ Next: Dungeon generation ╠═══


Author: Neel Raiyani [Roy Fokker]

Created: 2019-10-24 Thu 17:38

Validate