The player can do everything that an ordinary creature can do, and so we will create a Player class that inherits from the Creature class but has a few player-specific properties. For example, ordinary Creatures do not need to be able to level up (the xp value is just the amount they will give to the player upon defeat, in this case) but Players do. To that end we will add a level variable as well as appropriate xpToLevel and levelUp functions, which will calculate how much experience the player needs to level up, and will actually perform the levelling itself, respectively.

By using JSON files we’ve made it very easy to extend the game, but we’ve also gained the ability to easily save and load game data. Whilst we don’t want to modify the game files themselves, we can easily create a similar file that contains all the changes the player has made to the game world. We can then load this file using functions much like the usual JSON load functions, and use it to overwrite the original loaded game files, thereby incorporating all the changes that the player has made and creating a very simple save/load system! Because only the player can make these changes, we will add these loading functions to the Player class. This also allows us to have a separate save game per character, simply by naming the save file with the same name as the player.

Two additional things to note are the className and visitedAreas variables. Like many RPGs, ours will contain a class system which is used to assign a role to the player, in this case either that of a Rogue or a Fighter. The player will be able to choose their class (not the same as the C++ class, sorry for the confusing terminology) when they first start the game, and this choice will affect which of their attributes increases the fastest when they level up. In our very simple system the Fighter will have their strength increase faster than their agility, and the Rogue will have their agility increase faster than their strength. This small choice is very easy to implement and adds a bit of depth to the game. The visitedAreas variable is tied to the saving system and will store the ids of each Area the player has visited (and hence possible affected). Each of these Areas is assumed to have been modified by the player, and so will be saved to the save file. We use the std::unordered_set class to store the ids because it ensures that each id is only stored once, and it also doesn’t care about the order the ids are entered (and neither should we because JSON doesn’t either).

First we will look at the saving and loading functions.

The constructors are quite self-explanatory, and just initialise the new member variables. The third constructor takes two JsonBox::Values instead of one however, and calls two functions; the first is the actual save data for the player (their hp, inventory, and so on), whereas the second is for saving and loading the modified areas, as mentioned above. The toJson functions is also very simple, and just calls the Creature version of toJson before appending the new variables on the end.

The save function takes care of both the standard and area saving. To save the modified areas, the function iterates over each of the visitedAreas and creates JSON representations of them, which are then added to a new JSON object using their id as the key. By using std::unordered_set we have assured that all the ids are unique, and so no overwriting will occur here. Finally the JSON object is written to a file named the same as the player but with an "_areas" suffix appended to it. Finally, the loadArea function does the reverse and iterates over all the areas saved in the "player-name_areas.json" file, adding each of them to the the visitedAreas list. It also uses the EntityManager to call the load function on each new area; this overwrites the original Area (which will have already been loaded by the EntityManager when the program started) with the changes made by the player, essentially loading the save.

Next we have the two levelling functions.

In our RPG, the experience required to advance to level $n$ is independent of class, and equal to $1.5 n^3$. (This is far from a perfect formula, and most systems are far more complicated.) When the player levels up we first compute a stat multiplier for each attribute (hit points, strength, agility). The hit points multiplier is always 13.0, whereas the strength and agility multipliers are either 8.0 or 6.0, depending on the player’s class. Each multiplier $k$ is then fed into the slightly scary formula $\left\lfloor1+k\tanh\left(\frac{n}{30}\right)\Big(1+(n \mod 2)\Big)\right\rfloor$. This function and its cumulative form looks like

Attribute increases per level

Total attribute at given level

As you can see, the attribute increases spike at every other level but still slowly increases as the player’s level goes up. This gives us a levelling system that seems a little bit random, but is actually quite regular. Each stat is then increased by the calculated value, and finally the new attributes and level are reported to the player.

Don’t add any functionality to load the player as an entity like with the Creatures, we’ll be doing that in a different way to handle the save files.