Skip to content

Modding support

Nikolai Wuttke edited this page Jul 17, 2022 · 19 revisions

Like the original DOS executable, Rigel Engine supports replacing individual files from the game's data pack file (NUKEM2.CMP) with external files. This makes it possible to use existing patches and mods. On top of this, Rigel Engine features enhanced modding support. This includes high fidelity replacements (as of v0.8.0) and a mod manager (as of v0.8.5).

High fidelity replacements: Many assets can be replaced with files in modern formats, and the replacements can be higher fidelity. For example, graphics can be replaced with PNG images that have a higher resolution and alpha channel, music can be replaced with MP3 files etc. For graphics, the game seamlessly mixes assets of different resolutions. In other words, you can use a mixture of 4k backgrounds and original resolution sprites, if you so desire, and everything will be combined together properly (the high res art will appear at high resolution, while original art will be upscaled).

Mod manager: This makes it possible to organize mods using directories, enable/disable individual mods from within the game, and specify the order in which mods are loaded.

How to install mods

As of version 0.8.5, there are two ways of adding mods/replacement files to the game: In the mods directory, or at the top level. The former is the recommended way.

mods directory

The game looks for a directory called mods within the game directory (i.e. where NUKEM2.CMP is located). If it exists, every subdirectory of the mods directory is considered a mod. Here's an example:

Directory structure of game files with mods

This directory structure will be reflected in the game's mod manager as follows:

Mod manager in the game

Files within each mod directory are treated as replacement files. E.g., a file called L1.MNI in a mod directory will be used instead of the corresponding file from NUKEM2.CMP. If multiple mods are enabled that all contain a version of the same file, the version from the bottom-most mod (as per the ordering in the options menu) will be used. In other words, mods can override content from other mods, and the order is determined via the mod manager in the options menu.

In addition to replacing files from the .CMP file, a mod directory can also contain replacements in modern formats. These need to follow the naming scheme described below. For example, a file called kickbuta.mp3 would replace the music file KICKBUTA.IMF.

Top-level replacements

In addition to the mods directory, it's also possible to put replacement files directly next to the .CMP file. This matches the modding support offered by the original DOS version, and was the only option offered by older versions of RigelEngine (before 0.8.5).

It's also possible to use high-fidelity replacements at the top level, but the files must be placed in a directory called asset_replacements. This is different from the mods directory approach, where high-fidelity and original format files are placed next to each other.

Top-level replacement files will only be loaded if the corresponding checkbox in the options menu is ticked. However, it's not possible to control the loading of individual files at the top level, it's an "all or nothing" situation. Because of that, I would strongly recommend using the mods directory approach nowadays.

Complete example

Here's an example which consists of both top-level and mods-dir replacements:

Directory structure for all mod locations

Creating mods

Replacement file naming schemes

I also made a video demonstrating high-fidelity replacements, and the naming schemes used: https://youtu.be/bqF6EVP2qoA Note that the video was made before the introduction of the mod manager, and is thus using the top-level approach.

Type Naming scheme Supported formats Notes
Tile sets tileset<N>.png PNG <N> must match the (hexadecimal) number of the tileset to be replaced. E.g. tileset2.png to replace CZONE2.MNI, tilesetA.png to replace CZONEA.MNI, etc.
Backdrops backdrop<N>.png PNG Just like with tile sets, backdrop1.png replaces DROP1.MNI etc.
Sprites actor<N>_frame<F>.png PNG <N> is the actor ID and <F> is the animation frame. For example, the blue guard enemy has ID 159. To replace all animation frames of that sprite, files named actor159_frame0.png up to actor159_frame12.png should be provided.
Sound effects sound<N>.wav Wave <N> is the sound effect number, starting at 1. See note below on intro movie sound effects.
Music see notes Ogg, Mp3, Flac, Opus, Wave, various module formats To replace music, the filename needs to match the name of the game's original music file, but with one of the supported formats' extensions instead of IMF. E.g. kickbuta.mp3 to replace KICKBUTA.IMF. A variety of file formats is supported, essentially any format supported by the SDL_mixer library. This includes tracker module formats supported by ModPlug.

Limitations

  • It's currently not possible to replace full screen images like the menu background or episode end screens, except by replacing with files in the original game's format.
  • It's not possible to replace the large menu font with a modern format.
  • Using any high-resolution graphics forces nearest neighbor scaling.
  • In-game text, like the tutorial messages when picking up items, can't be changed except by compiling a custom executable.

Intro movie sound effects

To replace the sound effects used in the intro movie (Duke at shooting range), use the following:

  • sound35.wav - gun shot sound
  • sound36.wav - gun shot sound (2)
  • sound37.wav - falling shells
  • sound38.wav - target moves
  • sound39.wav - target stops
  • sound40.wav - "I'm"
  • sound41.wav - "back"

This is different from the naming of these files in the original game data, and these numbers are not shown in editing tools. I just defined them this way for RigelEngine.

Notes on image size and aspect ratio

Replacement graphics have no restrictions w.r.t. color, the full RGB spectrum can be used. Files can contain an alpha channel, and will be rendered accordingly. Replacement graphics can either match the resolution of the original graphics, or be higher resolution.

The way this works is that the engine determines the on-screen area that should be occupied by a graphic like a sprite, and then stretches whatever graphic is used (replacement or original) to fill that area.

For example, Duke Nukem's idle pose is 32x40 pixels in the original art. When running the engine at, say, 1920x1080 resolution, this will be upscaled to 144x216 pixels. Now if a replacement sprite is used, it will also be displayed as 144x216, regardless of the actual size of the replacement.

One important point is that the scaling takes aspect ratio correction into account: The original game's 320x200 resolution is stretched vertically when playing on a period-appropriate CRT monitor with 4:3 aspect ratio (320x200 is actually 16:10 without the stretching). To faithfully recreate the look of the game, RigelEngine therefore replicates this vertical stretching.

Now this means that if you were to replace the aforementioned Duke sprite with a replacement that's twice the size, i.e. 64x80, it will also undergo the same vertical stretching. With a replacement that already matches the aspect ratio of the on-screen display this won't be the case. E.g. 72x108, 80x120. These example sizes have an aspect ratio of 2:3, which matches the on-screen display of the sprite (144x216 at 1080p), whereas the original art (32x40) has an aspect ratio of 4:5.

Now how do you figure out what size to use for replacements in order to avoid vertical stretching? The easiest is to use the original art's size scaled by a factor of 5 horizontally, and 6 vertically. These scaling factors transform images into their aspect-corrected equivalents without any artifacts, since the scaling is by integer multiples on both axes. For full-screen images (320x200), this gives us a resolution of 1600x1200. For the Duke sprite example, it gives us 160x240.

Alternatively, you can decide to target a certain resolution like 1080p or 4k, and then determine the scaling factors that the engine will use. In the example above, I mentioned that the Duke sprite will occupy 144x216 pixels on-screen after upscaling when running at 1080p, but how did I determine these numbers? With the following formula:

scale_x = screen_height * 4/3 / 320
scale_y = screen_height / 200

So for 1080p, we would calculate the horizontal scale as 1080 * 4/3 / 320 = 4.5, and the vertical scale as 1080 / 200 = 5.4. If we then apply these factors to the Duke sprite, we get the numbers we used: 32 * 4.5 = 144, 40 * 5.4 = 216.

With this method, it's possible to determine the ideal size for a replacement graphic to appear on screen without any scaling at all at a certain resolution. If someone plays the game at a different resolution than the one you targeted, there will still be scaling but the aspect ratio of your art will be preserved.

Backdrops

Backdrops are handled a little differently compared to other graphics, in order to allow for wide backgrounds. The vertical stretching mentioned above will only be applied if a replacement backdrop matches the original art size, i.e. 320x200. If the replacement is larger on any axis (i.e. 640x200, 320x240 etc.), the graphic will be scaled uniformly in order to have it fill the entire screen vertically.

For example if you have a backdrop that's 216 pixels high and you're playing at 1080p, then it will be scaled by a factor of 5 (1080/216 = 5) both vertically and horizontally, regardless of the horizontal size.

How to extract assets

To extract assets from the original game and convert them to modern formats, various tools can be used. I can recommend K1n9_Duk3's Enormous Tool: It's a level editor, but also allows browsing the game's various assets and exporting them. It's also a good source for figuring out the numbers of sound effects etc. Unfortunately, it only runs on Windows.

Some other tools that I'm aware of: