Categories
Eldritch 2

Worldgen Documentation

I thought it would be interesting to share some of the internal documentation I’ve been writing for myself, as the systems in my engines and games have grown complex enough that I can’t keep them all in my head. This is my internal world generation document:

(See Four Letter Words for reference on the codenames here.)

Overview

Worldgen in Rosa is a big system that has grown substantially over the course of multiple games and game jams. There are multiple ways to use it; this document covers some of that but is primarily focused on how to use it for Tofu.

Worlds and Rooms

A level in Rosa typically consists of a world comprising multiple rooms. The rooms in a world may be placed by hand, generated from definitions, or a combination of both. Rooms are the atomic element of the worldgen process: modular chunks of level space with geometry, spawners, and navmesh.

World files can be empty, in which case they exist only to define their boundaries; or they can contain hand-placed rooms or even geo. (World files with geo were developed for Fray, where a subworld generator would add its geometry to its containing superworld room; for various reasons, I’ve abandoned that workflow for Tofu.) For Tofu, world files should typically be empty, with rooms added via config defs.

Room files should typically contain geometry, spawners, navmesh, and portal tags. For hierarchical worldgen, they can also contain subworld generators.

Special Cases

Completely static levels can specify a room file as a world file. This is mostly reserved for title screens, but would also be appropriate for building a non-procgen game like Neon.

Subworlds and Furnish Rooms

A room can contain one or more generators, which will generate a subworld on a smaller grid. This was developed primarily for indoor/outdoor spaces, but is theoretically extensible to any number of hierarchical tiers. In Tofu, this is used to build Eldritch-esque dungeons within larger outdoor spaces, and to populate open spaces with random junk (furnish rooms).

Furnish subworld generators function similarly to ordinary subworld generators, but they do not create sectors and portals; their geometry and navmesh are added to their containing room/sector. They are mainly intended to add random rubble to open spaces. In theory, they can be complex worlds in and of themselves, with highly specific portal tags to generate coherent complex spaces; but in practice, I found that that was extremely expensive and created a substantial content overhead. For Tofu, all furnish rooms for a given space should be designed to safely connect to all others.

Sector/Room Distinction

For most of Rosa’s history, sectors and rooms were functionally equivalent; a room always created a sector at the end of worldgen, so the relationship was 1:1. Since adding furnish rooms (in late 2023, for the abandoned Rasa project), that is no longer true. Furnish rooms are modular chunks of level data which get added to the sector of their containing room; it is no longer safe to assume that generated rooms and generated sectors have common indices.

Portals, Colors, Tags, and Themes

Rosa worldgen has gone through many iterations based on different needs of different games. The current system blends elements of Eldritch and Slayer Shock’s worldgen, along with features I discovered I needed in Loam and Lily and Fray.

Portal tags were the primary way to describe room connections in Vamp, but they impose severe restrictions on room selection and are one of the fastest ways to cause worldgen failures (and/or explode content needs). I continued to develop portal tags in Loam, and they have robust rules, but they are virtually irrelevant in Tofu beyond possibly being used in association with colors for splitting the boss dungeon into two halves (outside the boss/inside the boss).

Colorization is used to segment a world into discrete islands which are connected only via desired portals. It theoretically has a lot of utility for Metroidvania-style maps, but it will probably only be used in Tofu for splitting boss dungeons into two halves (outside the boss/inside the boss).

Tags are arbitrary descriptors ascribed to rooms, which can be used to filter or select spawners (via resolve groups, for filtering). Tags in a containing room are propagated to their generated subworlds; this is used in Tofu to distinguish each of the dungeons in a level and make their scripted entities connect to each other.

Themes are a recent addition based on the original feature from Eldritch. When a world is generated, it is randomly assigned one of an array of themes. Themes are used to weight room selection to prefer in-theme rooms over out-theme rooms, and they can also be used to select or filter spawners (via resolve groups, for filtering).

Generation

Rosa’s worldgen has grown into a complex process, but it follows a sensible order and clear set of rules.

If the world is a multiverse level (i.e., a shared online space in Tofu), it is seeded with the multiverse ID for consistent generation. Else, a random seed is selected.

Generation happens in two phases: first, rooms are generated (which can fail, and will retry until it succeeds), then those rooms are populated with entities (which cannot fail). The rest of this section is about generating rooms; populating them is mostly straightforward.

First, a theme is elected from any available options. This will be used to weight room selection.

Next, a world file is selected from any available options. For Tofu, I expect each level to only have one world file; anything else leads to a content explosion.

If the world file has any hand-placed rooms, they are added now. If it does not, there must be seed rooms in the world’s config; else there is nowhere to begin generation.

Next, seed rooms are added. These are essentially a configurable alternative to hand-placed rooms, which define the things a world must have to begin generation; but unlike hand-placed rooms, their location and orientation may be variable. This step can fail if the seed room placement cannot be fulfilled due to overlaps or mismatched portals.

Seed rooms and hand-placed rooms may be colorized, or may contain colorized portals; these are now used to build the color map of the world space. This is done by building a 3D Voronoi diagram from these rooms. This step will fail if all rooms of the same color cannot be connected (i.e., every color must be one singular island).

Next, procedural feature rooms are added. These are syntactically identical to seed rooms, but they are added after the color map is generated so that they can use colorization for filtering. This step can fail if the feature room placement cannot be fulfilled due to overlaps, mismatched portals, or mismatched colors.

Now, “fill rooms” are added to the world. For most worlds, this comprises the majority of the space. The map is filled out by iteratively finding any room that connects to existing portals (and does not divide the color map) and doing a weighted random selection of those options. Because rooms are authored assets of arbitrary sizes, there is no guarantee that the random selection will build a connected map. If this step finishes with unclosed portals, any unclosed “spacer” tiles are rolled back. (Spacer tiles were invented for Loam, to solve the problem of where to place doors between rooms and to provide space for connective rooms; but Tofu does not user spacer tiles in either its outdoor or indoor spaces.) This step can fail if the world does not have connective rooms available to fill the gaps.

If the world is not fully connected, it will now be connected with single-tile “connective rooms”. This is done by a randomly directed graph expansion from any open portals until all reachable space in the world is explored or an expansion limit is reached. The graph is then pruned of any paths that lead to dead ends, and each node in the graph is filled with a connective room. This step can fail if there is a one-tile gap between two rooms of different colors. (Connective rooms cannot bridge islands of different colors; that would violate the intended connectivity of the map.)

At this point, the map is validated to ensure that all rooms are connected and that each map color is a single connected island. This should usually be true, but this step can fail if the connective room expansion limit was reached before the map was fully connected, or if fill room expansion from differently colorized rooms created a map which could not be connected by connective rooms.

Optionally, a global transform is applied at this stage, to reorient the whole level to face a different cardinal direction.

Finally, this process is recursively run on any subworld generators in the generated rooms.