Skip to content

01. Introduction

LibLevelGen aims to provide an easy to use API to make level generation accessible to all, while still exposing advanced functions for people looking to do very specific things in their levels. In this section, we'll go over what you need to understand the API and the basics.

Prerequisites

The tutorials here assume you're already familiar with the following:

  • creating a simple Necrodancer (Synchrony) mod,
  • basic lua programming,
  • (very) basic understanding of object-oriented programming concepts.

I know it may seem tempting to go on with the tutorial despite not knowing these things, but keep in mind that some stuff may seem confusing if you do that. Anyhow, let's get started!

Main concepts

If you've done Synchrony modding before, you're probably familiar with the ecs, components, entities, etc... However, we won't actually be using any of that here. This is because of how the level loading pipeline works:

  • first, once the the level transition is initialized, the host generates the level in a special serializable format,
  • then, that level data is sent to other players if we're playing an online session,
  • finally, the level data is interpreted, and all the tiles and entities are placed.

What this means is that no tiles and entities are actually getting placed until the last step! Because of that, all the things you normally do to spawn entities like object.spawn do not apply here.

LibLevelGen does the work on the first step here - generating the level in the serializable format. It's actually similar to the built-in LevelBuilder module, which in a way is a "lite" version of LibLevelGen. You might think it's used internally here, but that's actually not the case - LevelBuilder is the one that was created later, even though it was released first.

The instance, the segments, the rooms and more!

LibLevelGen uses basic object-oriented programming to create its API. Generally speaking, there exists a hierarchy of object:

  • an instance is a bit of an abstract concept and serves as the "root" of the level - it provides some utility methods and holds all other objects (most indirectly), as well as some level metadata such as the music set to play in the level.
  • a segment is essentially some isolated part of the level. It will be explained later a bit more, though you might be familiar with it already since it directly translates to segments used during gameplay. A list of all segments in the level is kept in the instance.
  • a room is a single rectangular area in a segment, which has the top-left corner in the given x and y coordinates and specific width and height. Rooms are stored in a segment.
  • a tile is, well, a single tile. Tiles are kept in a room object, but unlike the previous objects which had no limit, a room can only have a specific amount of tiles - room.w * room.h. Rooms are also initialized with all the tile objects created immidiately, so you don't have to worry about checking whether a tile at the given coordinates exists or not (unless these coordinates are not in the room).
  • finally, there's the entity object. These represent entities that will be spawned once the level is loaded, and are stored in a tile object.

In other words, an instance can have an arbitrary amount of segment objects, a segment can have an arbitrary amount of rooms, rooms have a set amount of tiles and tiles can have entities. Hopefully, it should be pretty intuitive.

In the next part, we'll go over registering a new level generator, and creating all these objects mentioned above!