Enemies, areas and the difficulty index
In my post about the tile map generation I explained how each room consists of a 4 x 4 set of Area instances. I also explained that not every area has to be part of the room's path (i.e. it can be a solid block of tiles) and that most areas also have a chance of becoming a tunnel; a narrower play space which won't have enemies or collectables inside. Enemies also won't be placed in the start or end area of a room. The number of enemies spawned per room depends on the current level, but the number of areas available in that room to spawn enemies in depends on the tile map generation.
With that in mind, the pseudocode for placing enemies is as follows:
- Calculate a total difficulty index for the room based on the current level.
- Count how many areas are available to spawn enemies in.
- Assign all available areas an equal portion on the room's difficulty index.
- Randomly assign any leftover difficulty index resulting from integer division.
- For each area:
- Keep picking random enemy types until one is found with a difficulty index less than or equal to the remaining difficulty index for the area, and which can be appropriately placed in the area.
- Spawn that enemy into the area, taking into account the enemy type.
- Repeat until the sum difficulty index of all enemies in the area is equal to the assigned difficulty index for that area.
This algorithm assumes that at least one enemy type has a difficulty index of 1 to fill gaps in an area’s difficulty. Each enemy type has different logic to decide whether it is possible and where to spawn within an area. For example spikes will only be spawned adjacent to the floor, ceiling or walls of an area and will not be spawned covering the gaps used for travelling between areas.
Note that unless it were included in the "Is there somewhere I can spawn?" check for each enemy type, there’s nothing stopping enemies from being spawned on top of each other. In fact this currently happens a lot on the higher difficulty levels where there simply isn't enough floor/wall/ceiling space to accommodate all of the spikes.
While this tends to work quite well and could easily be expanded upon, there is a major issue related to the tile map generation. There are only minimal checks performed during the tile map generation to ensure that there are a reasonable number of areas that could house enemies. For example a room which connected the start and end areas exclusively with tunnels would be rejected since there'd be nowhere to place enemies or collectables. More sophisticated checks would need to be included if there were more complex enemy types (and more enemy types in general).
In keeping with the extensibility focus, there's actually functionality for the player to fire projectiles and for enemies to be damaged by and eventually die from these impacts, however the only enemy type in the game is the spikes, which cannot be killed.
Gotta get that sweet, sweet gold
There are 20 collectables per room and 4 rooms per level. If the player picks up all 80 in a single level they'll get 200 bonus points once they pass through the exit, meaning that there’s a total of 1000 points per level up for grabs.
The actual spawn logic for collectables is very simple. Collectables are only spawned in areas which can also contain enemies in order to force a risk/reward scenario. They are placed on a random tile within the area which isn't adjacent to a surface.
As with enemies there is nothing to stop collectables being spawned on top of each other. If a room has very few areas in which enemies and collectables can spawn, each of those areas will have a very dense set of collectables and enemies.
Oh God Why 2: Why, God, Why?!
Needless to say, I'm quite proud of the project and what I managed to do. I wanted to share the game and let other people experience it, which led me to write these articles and to start work on a PC port which is nearing completion. I'll likely finish it in the next few days, so stay tuned and you'll be able to muck around with Oh God Why yourself soon.