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
Entityclass, to manage information likeplayer's state and abilities. - Use an
Mapclass 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.
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"tcodconsole 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.
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.
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.
Links
╣ GitHub Repo ╠═══
╣ Home ╠═══
╣ Prev: Console and moving around ╠═══
╣ Next: Dungeon generation ╠═══