I’ve been working on several things in parallel the last days. I create a basic turn manager to get everything moving in turn from the start which helps me a lot to plan for enemies and other moving entities. And I also started working on the dungeon generator, or this was the majority of code written.
turn manager
The turn manager is quite simple but effective. First I created two helper classes as a base for all moving objects in the game. The turn emitter, i.e. the player who can end a turn, and the turn receiver which are basically all other things like ai enemies, floating and flying stuff etc. I quickly changed that because in gdscript you are a little restricted when it comes to inheritance.
When a turn emitter or reciever enters the scene, it calls their correspondent function on the turn manager. The register_emitter function checks if the incoming object has a special set of methods and signals to make sure future calls to those class members are not invalid. The register_receiver checks only if the method process_turn is present in the object members.
When the turn emitter sends a request to end the current turn, the turn manager locks all input on the emitter and marks it ready. Then it checks whether all registered emitters are locked and ready. This way I can support a multiplayer session quite easily if I decide/try to make the game for more than one player.
When all emitters are marked as ready, the turn manager sends a signal to all registered receivers by calling their process_turn function. The receiver processes its tick for the current turn depending on what their class is and returns a boolean value to signal success or failure.
The turn manager will unlock and unready all emitters as soon as all receivers have been processed. Now the emitter can make its turn and the whole things starts over.
I simulated a session with about 100 emitters and over 100.000 receivers and the result was lightning fast. The emitters were timed with a random amount of milliseconds until they send the request to end the turn. This way I was sure that an async and out of order action handling of the emitters is possible and working.
I managed to get this whole system working in less than 45 lines of code.
dungeon generator
The major part of sunday and monday I worked on the PCG side of things. I decided to start with the procedural dungeon generation. And so far, I am really ok with what I did.
I started by creating the PCG server, a class that accepts different PCG rules to generate procedural content. Those rules are implemented as Resource objects.
The initial version of the dungeon generator rule supports a hybrid mode to mix rooms, smaller caves and large caverns. The generator starts by adding the entrance room for the current level and places a stair-up cell. It then continues to generate rooms and caves until a ratio of filled cells and overall available cells is met. Caves and caverns are generated by placing a blob of marked cells near a room and then a cellular automata algorithm works on those cells until a maximum number of iterations has been reached.
I have also added the option to create loops to avoid a strictly linear dungeon layout.
Doors are implemented using 4 states: open, closed, locked, hidden. Hidden doors are always presented as the touching wall tile and will only be revealed when the player searches the room. Locked doors can be unlocked using their corresponding keys. The lock & key algorithm always makes sure that the system is solvable, that means no key is locked behind the door it should unlock, but another key to a door somewhere else may be in the locked region.
The for the lock & key system I used a very fast flood-fill algorithm which will be usefull for the next steps I just started to implement:
- chasms
- water pits/streams
- lava pits/streams
The flood-fill algorithm always checks for reachability of important regions of the dungeon before committing the hazards to ensure the dungeon level is solvable.
My plan for the coming weekend is to finally implement those hazards and start to work on bridge generation. Bridges then should span over hazards to ensure solvability.
The processing time of a 79×79 dungeon map is currently about 0.4 seconds.
