Julie's Document Website / Arcadius Universe / Dungeons / Design Document
Publish Date
2024-12-08 17:55:00 +0200 UTC
Author
Julie

Design Document

Table Of Contents

Abstract

The Dungeons, generally, is a rougelike feature of ArcadiusMC, similar to games like Hades, and Pressure (Roblox), with ULTRAKILL serving as inspiration as well.

Players will enter the randomly-generated dungeons, fight through rooms of enemies until they find a boss room, fight the boss and then advance on to the next level.

The purpose of this document is to outline the methods of implementation for the dungeons.

Dungeon Structure

The structures that will comprise the Dungeons' levels will be made with the "Arcadius-Structures" plugin. This plugin is developed in house and provides support for "Structure Functions" which the dungeon generator can later use to add elements to levels that did not exist before.

The Dungeons is made up of "Room Pieces", which are connected to eachother with "Doorway Pieces". This should be simple to understand, rooms are connected with doorways. These doorways are demarked with the dungeons/connector structure functions.

Each dungeon piece can have 1 parent piece, an entrance, that it originates from, and many child pieces, the room's exits. This forms the Dungeons as a node-tree structure.

Generation

This section describes the generation process of the dungeon levels.

To even begin, the generator takes in a config that specifies parameters that will be referenced in the coming sections. This config also specifies a list of "Structure Types". With each denoting a structure name and kind.

List of Structure Kinds:

ROOT
The root room, from which all other rooms are generated from.
MOB_ROOM
A regular room that is intended for combat, looting. The main room of action for players
BOSS_ROOM
Rooms that a boss is meant to spawn in.
CONNECTOR
A hallway, stairway, or any other type of room that is meant as a transitional space.
CLOSED_GATE
A closed doorway between two rooms.
DECORATED_GATE
A closed and decorated doorway between two rooms.
OPEN_GATE
An open doorway for connecting 2 Room Pieces.

Initial Tree Generation

NOTE: This is a messy and incomplete, but general, explanation of the generation process.

This section will walk through the process of generating the initial tree structure of a dungeons level.

For the generation process, a Queue will be used to perform generation in a breadth-first pattern. This queue will contain objects that contain:

originDoor: doorway
The Doorway the generation piece is being placed at.
sectionType: enum
The type of the section, one of: connector, or room
sectionDepth: int
The amount of pieces in this section placed one after another.

First, the generator is initialized by picking a ROOT room at random. If no root room found, generation fails. All the doorways of that root room are then placed into the generation queue. Initialization is now finished and the generator enters a loop that continues until the structure has finished generating, with each loop being considered a "step".

For each step: Poll the first element in the generation queue, if the queue is empty, generation is finished, stop step. Else move onto the genStep. Depending on the result of the gen step, the following happens:

SUCCESS
The generated piece is attached to the doorway it was generated for. If the piece is a connector, all exits are kept open, otherwise, a random amount of exits between config.minRoomExits and config.maxRoomExits are closed.

All exits still open are then added to the end of the generation queue.

FAILED
The originDoor is closed and this branch of the structure tree is considered "complete",
MAXDEPTH
If the piece's section depth is less than the "optimal" depth (A random value between the section's min depth and max depth) then rewind the generation back to the root piece of the section and restart generation from there.
MAX_SECTION_DEPTH
Flip section type and place back into the queue, this time to the front.

Repeat until generation queue is empty. Then move onto structure optimization.

genStep

If the current total structure depth is greater than allowed by the config, return a MAXDEPTH. If the current section's depth is greater than specified by the config, return a MAX_SECTION_DEPTH error.

Get a list of all potential pieces for this context. If current section's type is connector, then only get connector pieces. Drop all room types that have already been used in the current section. If no rooms have been found, or if all possible rooms have already been tried, return a FAILED error.

Otherwise, iterate through each room type and each doorway of each room. Randomize iteration order of the doors. For each doorway, align the room by that doorway to the originDoor and then check if the room would overlap any existing room pieces. If not, return a SUCCESS result. Otherwise, keep iterating. If no rooms match, return a FAILEd error.

Structure Optimization

This is a small set of operations performed to make the structure better for players and add missing pieces.

Attach boss room
The first post processing pass is to add a boss room. If no boss room types were specified in the config, do not perform this pass. Otherwise, find all room exits that are not connected to any other rooms, sort them by order and iterate through them. Try placing a boss room at each exit, if a valid placement is not found, move onto the next one, if it is, the boss room's location is found.
Drop dead ends
This pass uses a loop to iterate over all connector type rooms in the current structure and drops them from the structure if they have no exits, or have no exits that are attached to other rooms.
Close ending gates
Iterate over all dead end exit doorways and close them.
Decorate gates
Iterate over all dead end exit gates and randomly swap out closed gates with decorated gates.

Structure decoration and placement

This section outlines the processors and functions used to transform plain dungeon pieces into decorated levels.

NOTE: This is a general explanation of the process, specifics make my head hurt.

Setup

Initially we create a BlockBuffer with enough space to cover the whole dungeons structure.

Passes

GenerationPasses are loaded from the config and can have any paramaters specified by the user. This section will define the types of generation passes supported and what config options they accept.

Session Flow

This section describes the process of dungeon sessions being created, playing, and closing.

Sessions are started with a player initiating the session. With the initiating player as the only player in the session. This player also becomes the owner of the session. The owner can then modify the session's settings, such as: max players in the session, dungeon settings, and any other future parameters.

When the owner of the session decides to actually start the dungeon, they initiate the generator. The dungeon then generates according to the parameter.

Session State

BEGINNING
The beginning phase of a session. This starts when a session is created by a player. In this phase, a player can add other players to their session, modify their dungeon generation settings.
GENERATING
The dungeon is being generated. This state starts when the owner of the session starts the generation process. When the state ends, all players are teleported inside.
ACTIVE
Dungeon session is active, players are inside the dungeon and fighting monsters, looting and whatever.
INACTIVE
Session is inactive.

Notes

  1. A BlockBuffer is an in memory representation of an area of blocks that can be edited async. This is to avoid modifying the world during generation, which is slow. BlockBuffers have a defined area of space that they can represent and they cannot be resized.