Into The Mists - Roguelike
Link to Repository
Skip to Section
Team Size: 1 individual project
Engine: Personal Engine in C++ with Opengl
Genre: 2D Roguelike
Into the Mists is a 2D ascii roguelike. Notable features include:
Self registering map generators
AI behaviors with A* pathing and faction loyalty
Development Time: 8 weeks
Game Mechanics Overview
Each turn, the player can move in one of the four cardinal direction or in the diagonals. If the player would collide with a wall, he loses a turn. If the player runs into an NPC, the player attacks the NPC. The player can also choose to open doors or pick up items on his turn. After each turn, every NPC takes an action.
Letter NPC Character
+ Door Closed
- Door Opened
H: Move W
J: Move S
K: Move N
L: Move E
Y: Move NW
U: Move NE
B: Move SW
N: Move SE
Space: Progress message bar
,: Pick up item
O: Operate nearby feature or door
Map Generation Overview
Each map generator contains a static self registrion object that allows each map generator to add itself to the std::map of map generators without the class containing the std::map having to have a .h include to each generator. The base map generator class then creates each generator when called on startup. Each child generator inherits the generateMap function. GenerateMap is called when the map type tied to the map generator is called to be created via the in game menu.
The code below show the header file of the BaseMapGenerator which other generators inherit from. It also contains the MapGeneratorRegistration class. Each time a MapGeneratorRegistration is constructed, they add a reference of themselves to the s_mapRegistration which is the handle each map gets to be referenced outside of the class without having to include a .h to the game.
Upon initializing the game, create generators is called. For each map generator registered, a map generator is constructed with createMapGenerator and placed in s_generatedMaps.
Example Generated Map - Buildings with River
The map above is called Buildings with River. Initially, the plan was to make the water a river, but I kept the water in a lake setup due to lack of development time. The map has fog of war, making everything outside of the player's view darker. Light falls off the farther from the player it is. Building floor tiles are represented by light brown, light grey is walkable outside tiles, dark grey is building wall tiles, and dark green is outside wall tiles.
The base of the map consists of the outside floor and outside walls which are generated with cellular autamata. Afterwards, buildings are placed randomly. Each building on each of its walls have atleast one doorway if the wall is connected walkable space. Multiple doorwayss will appear on the same wall if the wall is connected to two or more sections of walkable space.
After each building is placed, the water is generated. Finally, items, doors, and NPCs are added to the map.
In generateMap below, initializeRandomData creates the cellular autamata, processOneStep creates one building, and addRiver adds water to the map.
NPCs are added to a map during on load or during the map generation process. NPCs are created using factories. When a map generates its NPCs, the map checks the tile type then creates an NPC appropriate for that tile.
Upon initializing the game, each file of extension type blueprint.xml is found in the Data/NPCs/ folder. For each file found, a new factory is created.
The above XML is an example of what is fed into the NPCFactory in order to create new NPCs. The XML defines the name, where it can spawn, what faction it belongs to, what the creature looks like (color and glyph), the NPC's attack values, and what behaviors are available to the NPC.
Behaviors self register in the same manner as the map generators. Each behavior calculates it's importance with the calculateImportance function and that is given back to the calling function to help choose what behavior to act out when Think is called.
Below is some examples in the Steal behavior.
Each NPC and the player has thier own faction. As the player attacks a certain faction and is caught doing so, the player become more loyal or less loyal to the other factions. A friend of an enemy is an enemy and an enemy of an enemy is a friend kind of mentality.
Faction loyalty is stored in:
std::map< PersonalFaction, std::map< OtherFaction, HowMuchPersonalFactionLikesOtherFaction>> FactionManager::s_factionLoyaltyMap;
In the video below, the orc 'O' and goblin 'g' follows the player until the player '@' walks over lava, then they lose interest in the player. Once the demon 'd' notices the player, he follows him, even through lava.
Saving and Loading
The code below shows how every object is saved and loaded. The XML reader used for the process is TinyXML.
Below is the code sample of how the agent class is saved an loaded.