A mod changes OpenReliant without changing the game's files on disk. It's a folder or an archive in
the game folder's mods folder. Its files replace the game's files that have the same names, or add
new ones, and its scripts can change how the game plays.
- What mods can do lists every feature on one page, with an example mod for each.
- This guide explains a mod's files: how they replace the game's, and how to make each kind.
- Scripting explains scripts, and the scripting reference lists everything they can use.
examples/modsholds example mods, with comments in their files.
New to modding? Make a first mod, then copy the example closest to what you want to make and change it. The other sections can be read in any order, as you need them.
Improvement: the original can't load mods.
- A first mod
- Where mods go and the manifest
- Load order and the mods screen
- How files are replaced, their formats, music, sounds and movies
- Textures and material maps
- The interface: shapes, pictures and fonts
- Faces and voices
- New ships, guns, missiles and pilots
- Game variables
- Models from OBJ and glTF files
- Missions
- Scripts
- Tools
- Sharing a mod
- Log messages and when something goes wrong
This mod replaces the main menu's music.
-
In the game folder, next to
resource.hog, make a folder calledmods, and in it a folder for the mod, such asmods/my-music. -
Add a manifest,
mod.ini, which names the mod on the mods screen (The manifest):[Mod] Name=My Music Version=1.0 Author=Me Description=A new main menu theme.
-
Add the files the mod replaces or adds, in the same folder. Here that's a WAV file called
New_Pensive.wav, which replaces the main menu's music (Music). -
Start
openreliantfrom a terminal. The log in the terminal lists each mod and what each of its files does (Log messages), and GAME OPTIONS, then MODS, shows the mod (The mods screen).
OpenReliant reads the mods as it starts, so start it again after changing a mod. In the developer mode, scripts and shaders reload as soon as they're saved (Reloading).
Mods go in a folder called mods in the game folder, next to resource.hog. Each mod is one of:
- a folder of files, which is the easiest to work on;
- an archive: a
.hogfile in the game's format (.HOGarchives), whichsltool hog packmakes from a folder (Sharing a mod).
StarLancer/
resource.hog
mods/
coyote/
mod.ini
mod.png
USA_Coyote.SHP
music.hog
music.hog.sha256
A mod's name is its folder's name, or its archive's name without .hog: coyote and music above.
What the mod adds or registers is named after it (Qualified names).
A folder mod is read just as the archive packed from it would be:
- Its files must be directly in the folder. Files in subfolders are skipped, as
sltool hog packskips them, and the log says so. - File names must be plain names in printable ASCII, with no colon, like an archive's member
names. Hidden files, such as
.DS_Store, are ignored. - A file that holds RefPack-compressed data, such as a file extracted with
sltool hog extract --raw, is decompressed as it's read, like a compressed archive member. Movies and pilots' faces, which the game reads uncompressed, are read as they are.
--no-mods starts the game without any mods.
A mod describes itself in mod.ini, an ini file like starlancer.ini, in its folder or archive.
Every key is optional, and keys can be written in any case:
[Mod]
Name=Coyote HD
Version=1.0
Author=Someone
Description=The Coyote, remodelled.
Url=https://example.com/coyote-hd
OpenReliant=0.7
Requires=coyote-models| Key | What it is |
|---|---|
Name |
The mod's name on the mods screen and in the log. Without it, they show the folder's or the archive's name |
Version, Author, Description |
What the mods screen says about the mod |
Url |
The mod's web page, where players can find it and its updates |
OpenReliant |
The version of OpenReliant the mod needs, such as 0.7. An older OpenReliant skips the mod and says so in the log, and the mods screen leaves it out |
Requires |
The mods it needs, by their names in the mods folder, without .hog, separated by commas. The mod loads only if each of them is on and loads before it; otherwise it's left out, the log says so, and the mods screen lists it in red with the mod it needs |
The manifest's other sections list what the mod runs and adds:
| Section | What it lists |
|---|---|
[Scripts] |
The mod's scripts, by kind (Kinds of scripts) |
[Missions] |
The scripts that run with a mission (Kinds of scripts) |
[ShipTypes], [Guns], [Missiles], [Pilots] |
The ship types, guns, missiles and pilots the mod adds, each described in a section of its own (New ships, guns, missiles and pilots) |
[GameVariables] |
The game variables the mod adds, each described in a section of its own (Game variables) |
The original never reads a file called mod.ini, so an archive with a manifest still works with it,
and the manifest doesn't replace any game file. Neither does the thumbnail, mod.png
(The thumbnail), a licence notice called license.txt, or a Markdown file such
as README.md. These belong to the mod, so two mods that each carry a license.txt don't
replace each other's.
Mods load one after another, and a later mod's file replaces an earlier mod's file with the same
name. The mods screen sets the order (The mods screen). Mods it doesn't list,
such as ones added since, load after the listed ones, in alphabetical order of their names, ignoring
case. Names such as 10-ships and 20-music keep that order clear.
Mods take priority over all of the game's files, including loose files, so a mod's mission18.dte
replaces the loose missions\mission18.dte in a retail install. Scripts start in load order too,
and the hooks of a later mod run first
(Order, removal and errors).
GAME OPTIONS has a MODS button, right of ABOUT OPENRELIANT, which opens the mods screen. It lists the
mods in the mods folder, with what each one's manifest says of it. A check box turns a mod on or
off, and the arrows beside the list move the chosen mod up or down the load order. The mods load from
the top down, so a mod replaces the files of the mods above it. REFRESH reads the mods folder
again, to find mods you've added or removed while the screen is open.
- CANCEL CHANGES puts the mods back as they were when you opened the screen.
- OPTIONS opens the page of options the chosen mod offers, if its scripts declare one
(Options). The mod's scripts read what you set; the values are kept in
its storage file,
storage\<mod>.data. - The changes take effect the next time OpenReliant starts. RESTART TO APPLY shows while the screen's list differs from what's loaded.
- The panel shows the mod's thumbnail (The thumbnail), the mods whose files it replaces (REPLACES FILES OF), and the mods below it that replace its own (FILES REPLACED BY).
- A mod that won't load is listed in red, and the panel says why: a damaged archive
(Checksums), or a mod it needs (
Requires) that is missing, off, or below it. - A mod the catalogue on the web offers in a newer version is listed in gold, and the panel says UPDATE AVAILABLE IN GET MODS with the version. With CHECK FOR UPDATES ticked on the VIDEO tab (Newer releases), the screen reads the catalogue in the background as it opens, once per run; unticked, it reads nothing, and shows what GET MODS has read. GET MODS opens the catalogue on the chosen mod (Getting mods from the catalogue).
The screen keeps the order and which mods are off in starlancer.ini in the game's folder, in its
own section, one line for each mod: the mod's name in the mods folder, and 1 if it's on or 0 if
it's off. The lines are in load order:
[OpenReliantMods]
10-ships=1
coyote=0
20-music=1The mods the section doesn't list are on, and load after the listed ones, in the order of their names. Only 0 turns a mod off. To go back to loading every mod in the order of its name, delete the section. You can also edit it by hand.
For a single run, --mods loads only the mods it names, in that order, and keeps the screen shut,
so the section doesn't change. --mods bananas plays a mod on its own, and a path, such as
--mods dist/bananas.hog, loads a mod from outside the mods folder
(Configuration).
- A mod whose name has an equals sign, starts with a bracket or has spaces at either end can't be listed. It stays on and loads with the unlisted mods.
- The screen lists up to 255 mods, and leaves out the ones that need a newer OpenReliant (The manifest).
--no-modsloads none, and keeps the screen shut.- A screenshot taken with
--screenshotfollows the section too, so it loads only the mods that are on.
GET MODS, above the list on the mods screen, opens the catalogue of mods on the web: the mods that
OpenReliant's mods page lists in its mods.json
index, and those of any other repository you add (Repositories). The screen lists
them like the mods screen does, grouped under the site's top categories (SHIPS, MISSIONS, ...); a
click on a heading folds the group, and another unfolds it. The panel shows the chosen mod's
thumbnail, version, author, full category, the repository that lists it, the OpenReliant version it
needs, its size and its description. INSTALL downloads the mod's archive into the mods folder,
checks it against the digest the catalogue gives, and writes the digest next to the archive as its
checksum file (Checksums), so the archive is checked again each time OpenReliant
starts. UPDATE does the same for a mod that is installed in an older version, shown in gold in the
list, with the panel saying which version is installed and which one the update brings. A mod that
needs a newer OpenReliant is red and can't be installed. A mod you copied into the mods folder as
a folder is left alone, and the panel says so, since OpenReliant would load both the folder and the
archive. RELOAD reads the repositories again. One download runs at a time: INSTALL and RELOAD wait
for it. While the mod you chose downloads, INSTALL reads CANCEL instead: it stops the download and
leaves nothing in the mods folder, and the panel says CANCELING for a moment while the download
stops. Leaving the screen lets a download go on. OK goes back to the mods screen, which now lists
the installed mods. Like every change on the mods screen, they take effect at the next start.
The catalogue and the downloads run in the background, so the menus keep working, and the panel
shows the download's progress. Besides the check for a newer release (Newer
releases), OpenReliant connects to the web only for this screen:
to read the repositories, and to download the mods you install. Everything is downloaded over HTTPS:
a plain http link is changed to https before it is requested, and so is every redirect, so
nothing is read in plain text, and plain HTTP never replaces HTTPS when HTTPS fails. Each request
names OpenReliant as the program asking and carries nothing else about you or your game. Your own
machine (such as localhost, 127.0.0.1 or ::1) is read only for a repository there, along with
the thumbnails and archives its index links, and over plain HTTP. A repository on the web can't
point OpenReliant at your machine: such a link, or a redirect to your machine, fails with THE
CATALOG POINTS AT THIS COMPUTER. A server that stops answering fails with THE SERVER DOESN'T ANSWER:
an index or a thumbnail gives up after a minute, or after 15 seconds with nothing arriving, and an
archive after 30 seconds with nothing arriving. RELOAD then reads the repositories again, and tries
again the thumbnails that failed. Quitting OpenReliant never waits for a download. The screen lists
at most 127 mods.
OpenReliant records the mods GET MODS installs in starlancer.ini, in the section
[OpenReliantInstalledMods], one line for each: the archive's name, and the repository it came
from. A recorded archive loads only with its checksum file next to it, and only if it matches; one
whose checksum file is missing or doesn't match is listed in red on the mods screen as damaged,
like a damaged archive you copied in by hand. A mod you copied in by hand isn't recorded, and loads
unchecked when it has no checksum file. Delete a mod's line to treat it as copied in by hand.
[OpenReliantInstalledMods]
viper.hog=openreliant-modsA repository is a mods.json index on the web. OpenReliant reads the repositories that
starlancer.ini names in [OpenReliantModRepositories], one line for each: a name of your
choosing, and the URL of its index, in the order the screen reads them.
[OpenReliantModRepositories]
openreliant-mods=https://openreliant.github.io/openreliant-mods/mods.json
mine=https://example.com/mods/mods.jsonWithout the section, OpenReliant reads its own repository, openreliant-mods, alone. With it,
OpenReliant reads what it lists, so you can add repositories, or leave ours out. An empty section
hides GET MODS. The screen lists the mods of every repository together. Where two repositories
list the same id, the first one listed wins, and the log names the mod left out. A repository that
can't be read is named in red above the list, with the reason, while the others' mods are listed.
mods.json is a JSON object with a format of 1 (the default when the key is missing) and a mods
list. A catalogue in a newer format tells the player to update OpenReliant. Each mod is an object.
id, archive, sha256 and size are required: a mod that lacks one is skipped, with a line in
the log. The other keys are optional, and keys OpenReliant doesn't know are ignored, as the
top-level generated key is, which says when the index was written.
| Key | What it is |
|---|---|
id |
The mod's name in the mods folder. Its archive is named <id>.hog. A mod is skipped when its id can't be a file name, starts with a dot, is one of Windows's device names such as con, even with an extension as con.v2, or can't be a key of starlancer.ini (an equals sign, a leading bracket, spaces at either end) |
name, version, author, description, openreliant |
The same values as the manifest's keys of those names (The manifest); openreliant is the OpenReliant version the mod needs |
category |
The site's category, such as ships/fighters/alliance |
updated |
The date and time of its latest release |
size |
The archive's size in bytes. The download stops as soon as more arrives, or when the server announces another size. A HOG archive holds at most 4 GiB less one byte (4294967295 bytes), since its offsets are 32-bit, so a mod with a larger size is skipped |
archive |
The URL of the archive, on any host. An http link is requested as https |
sha256 |
The archive's SHA-256 digest, as 64 hexadecimal digits, with or without the sha256: prefix GitHub gives a release asset's digest. The download is checked against it, and it is written as the archive's checksum file |
thumbnail |
The URL of its thumbnail, a PNG (The thumbnail) |
url |
The URL of its web page |
{
"format": 1,
"mods": [
{
"id": "viper",
"name": "Viper Mk II",
"category": "ships/fighters",
"version": "1.3",
"author": "Someone",
"description": "The Viper Mk II, flyable.",
"openreliant": "0.7",
"size": 5624773,
"archive": "https://github.com/OpenReliant/openreliant-mods/releases/download/viper-v1.3/viper.hog",
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"thumbnail": "https://openreliant.github.io/openreliant-mods/thumbs/viper.png",
"url": "https://openreliant.github.io/openreliant-mods/mods/viper/"
}
]
}Anyone can host a repository:
- Publish
mods.jsonover HTTPS. A static site works, such as GitHub Pages. - Link each archive over HTTPS, from anywhere: a release on GitHub, or any other host. Give its
sha256digest and itssize, which pin the exact archive the index lists, wherever it lives. - Keep the ids unique: a mod's id is its name in the
modsfolder, and OpenReliant loads one mod of a name. - Tell players to add the repository to
starlancer.ini(Repositories). To try it on your own machine first, serve the index athttp://127.0.0.1and link its archives and thumbnails there too: only a repository on your machine may link to it, and only your machine is read over plain HTTP.
The site generator of openreliant-mods, in
.github/scripts/site.py, writes the index from the collection's releases, and can serve as the
example.
A file in a mod replaces every game file with the same name, wherever the game keeps it: inside its
archives or as a loose file. Names are matched ignoring case, and only the file name counts, not the
folder: a mod's New_Pensive.wav replaces music\New_Pensive.wav. A file with a name the game
doesn't use is added, so the mod's new missions or models can use it. Where the game has the same
name in several places, such as the stats tables, which exist both as loose files and in
resource.hog, or a picture that was saved twice, those are copies of the same file. Where an
archive has the same name twice, the game reads the first one, and that's the one the mod replaces.
| What | Where the game keeps it | For example |
|---|---|---|
| Models | resource.hog |
USA_Coyote.SHP |
| Interface pictures, fonts and sprites | resource.hog, CD1.HOG, CD2.HOG |
brd2cd.tga, FONT.FNT, medal1.spr |
| Sound banks | resource.hog, CD1.HOG, CD2.HOG |
betty.fat |
| Music | music\ |
New_Pensive.wav |
| Speech | ms_speech\msspeech.hog |
ABRT_001 |
| Pilots' faces | pilots\pilots.hog |
45Tigers_Moose_D.fm8 |
| Movies | the game folder, interface\, inter\, CD1.HOG, CD2.HOG |
New_nms.bik, int.bik |
| Missions | missions\, resource.hog |
mission1.dte |
| Stats tables | the game folder | shipstats.bin |
Names have no folders, so give the files your mod adds a unique prefix, such as the mod's name, to
keep them from clashing with another mod's files. A file that mod.ini names with a folder in
front, written with \ or /, such as EngineSound=sounds\peel.wav, is found by its own name, on
every system: models, sounds, pictures and face films alike.
sltool hog ls <archive> lists the files in an archive, and sltool hog extract extracts them
(Tools).
A mod's files use the game's formats, which the developer documentation describes, and a few modern ones beside them:
| What | Formats | Made with |
|---|---|---|
| Models | .shp |
sltool shp from-obj or sltool shp from-gltf (Models) |
| Textures | PNG up to 16384 pixels a side, or DDS and KTX2 files compressed already | An image editor (Textures) |
| Interface shapes and pictures | PNG at any size | An image editor (The interface) |
| Interface fonts | .fnt, TrueType or OpenType |
A font editor (Fonts) |
| Music | WAV (16-bit PCM or IMA ADPCM), FLAC, MP3, Ogg Vorbis or Opus, at any rate | An audio editor (Music) |
| Sounds | WAV (16-bit or 8-bit PCM, or IMA ADPCM), FLAC, MP3, Ogg Vorbis or Opus, at any rate, mono or stereo, one at a time | An audio editor, from the sounds sltool fat extract saves (Sounds) |
| A mod's engine and gun sounds | WAV (16-bit or 8-bit PCM, or IMA ADPCM), at any rate | An audio editor (Ship types, Guns) |
| Lines of speech | WAV (16-bit or 8-bit PCM, or IMA ADPCM), FLAC, MP3, Ogg Vorbis or Opus, at any rate, mono or stereo; or the game's speech files | An audio editor, or sltool speech encode from WAV (Lines) |
| Pilots' faces | .fm8 face films |
sltool fm8 encode, from PNG frames (Faces) |
| Missions | .dte |
A mission editor (Missions) |
| Stats tables | .bin |
Better changed from a load script (The records) |
| Whole sound banks | .fat |
No tool builds one: replace a bank's sounds one at a time instead (Sounds) |
| Movies | WebM or Matroska, of VP9 video with Opus or Vorbis sound, or Bink (.bik) |
FFmpeg (Movies), or RAD Game Tools' Bink tools |
A piece of music is a WAV file (16-bit PCM or IMA ADPCM), or a FLAC, MP3, Ogg Vorbis or Opus file,
at any rate, named after the game's piece: New_Pensive.wav, New_Pensive.flac, New_Pensive.mp3,
New_Pensive.ogg or New_Pensive.opus replaces the game's music\New_Pensive.wav. A .ogg or
.opus file can hold either Vorbis or Opus. If a mod has more than one, OpenReliant uses the first
in this order: WAV, FLAC, MP3, Ogg, then Opus. FLAC keeps the music exactly as recorded in about two
thirds of the space of 16-bit WAV. MP3, Vorbis and Opus take less space still, but lose some of the
sound. A piece in any format other than WAV is decoded over the next few frames, so the game
doesn't stall, and starts once it is decoded, a fraction of a second later. If the game's piece
loops back to a point partway through, the mod's piece loops back to the same point in time,
whatever its format. Make it the same length as the game's piece, or at least as long as its loop point, to keep
the loop where the game has it. If the game's piece can't be read, the mod's piece loops back to the
game file's byte offset instead, which may be the wrong moment, and the log says so.
examples/mods/music replaces the main menu's music with a track made
for it, whose body starts where the game's piece loops back. The CD player in the rooms plays only
the pieces on its lists, which a load script can change and add tracks to (The CD player's
tracks), so the example gives its track a row there too.
Scripts can also play, fade out and stop music, and get notified when it changes
(Sound).
The game keeps its sounds in sound banks, .fat files that each hold a few WAVE files by number
(Sound banks lists what each bank holds). A mod replaces one of them
with a file named after the bank and the sound's number, with at least three digits:
stdsmp_012.wav replaces sound 12 of stdsmp.fat, and smp3d_067.flac replaces sound 67 of
smp3d.fat, GUN01, the Laser Cannon's shot. sltool fat extract <bank> <folder> saves a bank's
sounds under these names, so you can find the one to replace and start from it. sltool fat ls smp3d.fat names the 3D sounds that play each of that bank's sounds. If a mod has no file for a
sound, the bank's own sound plays, so a mod can replace just a few.
A sound is a WAV file (16-bit or 8-bit PCM, or IMA ADPCM), or a FLAC, MP3, Ogg Vorbis or Opus file,
at any rate, mono or stereo. If a mod has more than one for a sound, OpenReliant uses the first in
the same order as for music (Music). MP3 adds a little silence at the start and the end,
which you hear when a sound loops, such as an engine's, so the other formats suit sounds better. A
sound plays wherever the game plays the bank's, with the bank's priority, and at the volume, pitch
and place the game gives it. A 3D sound plays at its own pitch, whatever its rate. A stereo 3D
sound is only as wide as the thing it comes from: a big ship's engine sounds wide close by and
narrows to a point as the ship moves away, while a shot, a missile or an explosion is a point. With
--original, which uses the plain software mixer, a stereo 3D sound plays in mono so that it can
still be placed. Scripts can also play a sound file from their mod by name, in any of these
formats, in 2D or 3D (Sound).
A movie can be a WebM or Matroska file of VP9 video, named after the game's movie: warty_.webm or
warty_.mkv replaces the game's warty_.bik, the Warthog logo. If a mod has more than one,
OpenReliant uses the first in this order: WebM, Matroska, then Bink. A movie that can't be played
is left out, the log says why, and the Bink movie plays instead.
- Video. VP9 of profile 0: 8 bits, with the colour at half size across and down (4:2:0), which encoders make unless told otherwise. OpenReliant takes the colours as BT.601 in the limited range, as the game's movies have them, unless the video is tagged as BT.709 or full range. AV1 is planned (#1334).
- Size. A movie is drawn in the place and at the size of the game's movie it replaces, so a larger movie is sharper in the same place. Keep the game movie's shape, and use a whole multiple of its size, such as 1280x960 or 1920x1440 for a movie of 640x480. VP9 is decoded on the processor, on one thread, so a movie much larger than the window only costs time. A movie that replaces none of the game's, such as a new one that a script plays, is drawn at its own size.
- Rate. A movie's frames show at their own times, at any rate. The front end's transitions, the landing, the ITAC's movies, and the movies of the rooms, a new pilot's induction, the news reports and the locker play at 15 frames a second whatever the movie's rate, as the game plays them. Make those at 15 frames a second, with as many frames as the game's: the rooms show the crew on given frames.
- Sound. Opus or Vorbis, at any rate, mono or stereo, or none. It is decoded whole as the movie starts. Sound in another format, such as AAC, is left out, and the log says so.
- File size. A movie is read whole into memory as it starts. A file in a mod's folder can be at most 256 MiB.
With FFmpeg, this makes warty_.webm from a sharper copy of the movie,
warty_hd.mov, made with an upscaler say. -crf sets the quality: a lower number is better and
makes a larger file. Add -colorspace bt709 where the copy's colours are in BT.709.
ffmpeg -i warty_hd.mov -c:v libvpx-vp9 -crf 24 -b:v 0 -pix_fmt yuv420p -c:a libopus warty_.webmModel and effect textures come from the texture cache, tcachehw.dat (Texture
caches). Each has a name without an extension, such as yank_2, the Coyote's
hull. To replace a texture, add a PNG file with its name, yank_2.png, at any size, with an alpha
channel if it needs one. sltool tcache ls tcachehw.dat lists the names and sizes, and sltool tcache extract tcachehw.dat palette.tga textures yank_2 saves a texture as a PNG with its name and
original size, which you can use as a template.
- A picture must follow the model's texture coordinates, so keep the layout of the template, just with more pixels. The whole picture covers the same area as the whole original texture.
- Keep the template's proportions and use a whole multiple of its size: a 256x128 texture becomes 1024x512 or 2048x1024. A picture with other proportions is stretched over the model.
- OpenReliant generates the mipmaps when it loads the picture (each level half the size of the last, down to one pixel, computed in linear light and weighted by alpha), so you don't need to provide them.
- A side longer than 8192 pixels is halved until it fits, since every GPU supports that size.
- 2048x2048 is enough for a fighter.
Where the GPU takes compressed textures, as desktop GPUs do, OpenReliant compresses mods' pictures as today's games do: colours, material maps and emissive maps in BC7, normal maps in BC5. A 4096x4096 picture then takes about 21 MB of GPU memory with its mipmaps, a quarter of the 85 MB it takes uncompressed, and the computer lets go of its own copy once the GPU holds it.
- Compressing a large picture takes a few seconds, once. The result is kept in the game folder's
cache/textures, one file for each texture, and later starts read it in a moment. A changed picture, or another texture detail, compresses again. The folder can be deleted at any time. - A normal map keeps only two channels, x and y, in BC5 and uncompressed alike, so the extra value OpenReliant keeps for each normal map pixel (how much the normals spread out, which widens the highlights on fine details) moves to the material map's alpha channel.
- A 16-bit normal map is compressed from its 16 bits, which keeps more of a shallow slope than an 8-bit picture can (16-bit normal maps).
TextureCompression=0instarlancer.ini, or--uncompressed-textures, keeps the pictures as they are (Configuration).
A picture or a map can also come compressed already, in a DDS (.dds) or KTX2 (.ktx2) file with
the same name, which OpenReliant looks for before the PNG file: yank_2.dds, yank_2_normal.dds.
It reads a single 2D picture with its mipmaps, in BC1, BC3, BC5 or BC7, or uncompressed 8-bit RGBA,
up to 16384 pixels a side, as a PNG file can be.
A KTX2 file can be supercompressed with Zstandard (toktx --zcmp), but not hold Basis Universal
data (#637). Its colours are taken as
sRGB-encoded, as a PNG's are.
- A compressed file draws as it is, so it starts as fast as a cached one. Give it its mipmaps: OpenReliant can't make them from compressed pixels.
- Beside a compressed picture, a normal map in BC5 and a material map in BC7 are used as they are, and a PNG map is compressed to match. A map in another format is left out, which the log says.
- A map needs at least as many mipmap levels as its picture. Extra levels are dropped, and a map with fewer is left out, which the log says. A PNG file always has them all.
- A compressed picture needs a GPU that takes its format. Elsewhere, it is left out, and the cache's own texture is drawn.
Improvement: the original only uses the textures in its cache, at most 256x256.
The loadout draws the ships in green and the missiles and guns in red. The game keeps a green and a
red copy of each ship texture, named with a g or an r in front: gyank_2 and ryank_2 for
yank_2. Don't make them for your mod: OpenReliant makes the copies of your pictures as it runs,
in the same shades as the game's copies, and passes over any copy a mod gives, such as
gyank_2.png. A picture that comes compressed already, in a DDS or KTX2 file, can't be turned
green or red; the loadout then draws the game's own copy, where the game has the texture.
A texture can come with the extra maps that modern tools produce, which describe the surface's material, such as metal or paint, rough or polished. They follow the metallic workflow used by glTF 2.0, Blender and most engines:
| File | Contents |
|---|---|
yank_2_normal.png |
The surface normals, in OpenGL's convention: green points to the top of the picture |
yank_2_orm.png |
Occlusion, roughness and metallic in the red, green and blue channels, as in glTF |
yank_2_occlusion.png, yank_2_roughness.png, yank_2_metallic.png |
The same three as separate greyscale pictures, if there's no _orm map |
yank_2_emissive.png |
The light the surface gives off by itself, such as glowing vents or lit windows, in colour, as glTF's emissive texture: black where it gives off none |
Each map must be the same size as its texture, and any of them can be left out: a normal map on its own adds surface detail to the lighting, a material map on its own gives the surface its highlights, and an emissive map on its own makes parts of it glow. Without a map, the surface is treated as flat, without occlusion, rough, non-metallic and giving off no light. Normal and material maps hold linear values, not colours, and their mipmaps are computed that way. An emissive map holds colours, like the texture itself. For normal maps, each mipmap level renormalizes the normals and keeps how much the normals it averages spread out, so the alpha channel of a normal map is ignored.
OpenReliant lights each pixel according to the maps:
- The normal map tilts each pixel's normal. The models have no tangents, so OpenReliant works out the texture's directions on the surface for each pixel. Ambient light gets darker the more the normal is tilted away from the surface, so grooves and edges also show on the side facing away from the lights.
- The material map sets the highlights each light makes, from the surface's roughness and metalness, and metals tint them with their colour. This replaces the original's highlight pass.
- Surfaces reflect their surroundings, the sky and the nebula, blurred according to the roughness: smooth glass reflects the nebula clearly at glancing angles, and polished metal reflects it everywhere.
- Occlusion darkens the ambient light and the reflections.
- Where the normal map has details smaller than a pixel, the surface looks correspondingly rougher (Toksvig's method), instead of its highlights sparkling.
- The emissive map's colour is added once the pixel is lit: on the dark side of a ship it shows as painted, and on the lit side it brightens the surface. Keep it dark and soft for a gentle glow; a bright emissive map washes the surface out. The loadout draws its ships and weapons without their emissive maps.
The base texture should hold only the surface's colour, with no shading painted in, or the lighting
will show twice. The MATERIALS setting under VIDEO and --no-materials turn the maps off, as
--original does.
For metals, the base colour sets how much light the metal reflects, and tints its highlights and reflections. Real metals reflect at least half the light: steel about 56 percent and aluminium about 91, which is about 180 to 255 in sRGB. A metal painted darker than that reflects too little to look right. Put dirt and wear in the roughness map, or make those areas non-metallic in the metallic map, instead of darkening the colour.
Improvement: the original lights every surface the same way, with a highlight pass on top, and only its light maps make a surface glow.
A normal map can be a 16-bit PNG, as Blender, Substance and most baking tools can write it. OpenReliant keeps its 16 bits: a very shallow slope, such as a gently curved plate or a soft dent, then lights smoothly instead of in bands. An 8-bit normal map has only 256 steps in each direction, so a slope that changes by less than a step across many pixels comes out in stripes.
- Uncompressed, the normal map's x and y stay at 16 bits on the GPU, which takes no more memory than an 8-bit normal map.
- Compressed, OpenReliant makes the BC5 blocks from the 16-bit values. BC5 holds values between its 8-bit endpoints finer than 8 bits, so a shallow slope keeps much of its smoothness.
- Only normal maps are read at 16 bits. Other 16-bit pictures are read at 8 bits, which is all that colours, material maps and emissive maps need.
The interface draws its graphics from sprite sets (.SPR sprites): the flight
display from HUDHARD.SPR, each screen from its own set, and each ship's schematic (shown on the
display for the player's ship and the target) from a set such as ARCHSCEM.SPR. The screen
backgrounds are TGA pictures. A mod can replace either with a PNG picture of any size.
To replace a shape, add a PNG named the way sltool spr extract names it: the set's name, _, the
shape's number in the set with at least three digits, and .png. For example, HUDHARD_127.png is
the targeting cluster's arc. Use the extracted picture as a template:
sltool hog extract resource.hog files # the sprite sets, among other files
sltool spr ls files/HUDHARD.SPR # each shape's number and size
sltool spr extract files/HUDHARD.SPR shapes # each shape as a picture at its original size- Size and proportions. A picture is drawn over the same rectangle as the original shape, whatever its size. Keep the template's proportions and use a whole multiple of its size: the 68x141 arc becomes 272x564 at four times the size. A picture with other proportions is stretched to fit.
- Layout. The game positions shapes by that rectangle, so keep the art where the template has
it: art moved within the picture moves on screen, and art outside the template's edges gets
squeezed in. The template's margins are all the room there is. Shapes drawn on top of each other
must keep matching layouts: a ship's schematic (shape 0) and the hit markers on its four quadrants
(shapes 1 to 4), which flash over it; or a gauge's unlit shape and the lit shape drawn over it up
to the current level, such as the speed gauge,
HUDHARD_185.pngandHUDHARD_184.png. - One picture, several places. The game draws some shapes mirrored, such as the targeting cluster's right arc, which is the left one flipped, and some in several places. One picture covers all of them. A shape the game draws in several colours is a separate shape for each colour, each with its own picture.
- Alpha and colour. The template is transparent around the shape, and the picture's alpha channel is used, including soft edges. Pictures keep their colours, but are dimmed where the game dims the shape, such as a menu item that can't be selected.
- Resolution. The menus, briefing, ITAC and loadout screens are designed for 640x480, and each is scaled to fit the window. The flight display and the pause menu are drawn at VIDEO's UI SCALE, a share of that size, 80 percent by default and 100 at most, as in the table below. A picture is sharp when it's as many times larger than the template as the window scales it: four times is enough for the flight display up to 3840x2160 at the default UI SCALE, and for the 640x480 screens up to 2560x1440. Larger pictures gain nothing, and cost memory and loading time, which delays the first frame that shows them.
| Window | Flight display and pause menu, at 80% | 640x480 screens, and the display at 100% |
|---|---|---|
| 1920x1080 | 1.8 times | 2.25 times |
| 2560x1440 | 2.4 times | 3 times |
| 3840x2160 | 3.6 times | 4.5 times |
The screen backgrounds, the ITAC's pictures, the briefing door and the loading screens are TGA
files, as is the loadout's backdrop. To replace one, add a PNG with its name, such as
sl_splash2.png for interface\sl_splash2.tga, at any size.
- Size and proportions. A picture is scaled to the screen's height, keeping its proportions, and centred horizontally. The game's pictures are 4:3, mostly 640x480 (the loading screens have larger versions), so a 4:3 picture such as 1280x960, 2560x1920 or 2880x2160 covers the screen the same way.
- Widescreen. A wider picture extends past the sides of the screen in a wider window: a 16:9 picture, such as 1920x1080 or 3840x2160, fills a 16:9 window, where the game's pictures leave bars at the sides. Its middle 4:3 area (1440x1080 of 1920x1080) sits behind the screen, whose shapes, buttons and text stay where the game puts them, so put the important art in the middle and use the sides for scenery. A narrower window cuts off the sides, and a window wider than the picture shows bars beyond it.
- Opaque. Pictures are drawn without their alpha channel, like the original pictures.
Two TGA pictures are read pixel by pixel as data, one pixel for each star or for each colour of the sky. A PNG replaces one of these only if it has the same size; otherwise it's skipped, with a message in the log:
| Picture | Size | What it is |
|---|---|---|
space.tga |
360x360 | The star map |
starref12.tga |
256x256 | The sky colours |
Two more are drawn pixel by pixel at their picture's size. A PNG for one of these can be the
original's size or a whole multiple of it, the same across and down, up to 8192 pixels; another
size is skipped, with a message in the log. At 1024x1024, fpanels.png draws the loadout's panels
four times as sharp, with their text in the outline fonts (Fonts), and powerball.png
draws the flight display's power ball four times as sharp.
| Picture | Size | What it is |
|---|---|---|
fpanels.tga |
256x256 | The loadout's panels, the buttons and the scrollers |
powerball.tga |
256x256 | The flight display's power ball |
Improvement: the original only draws its interface pictures at their original size, and stretches its backgrounds to fit the screen.
The interface uses bitmap fonts made for 640x480 (.fnt fonts). A mod can
replace one with a TrueType or OpenType font with the same name, optfnt.ttf or optfnt.otf for
interface\optfnt.fnt, which is drawn at the window's resolution (Outline
fonts):
| Font | Used for |
|---|---|
optfnt.fnt |
Large text in the menus, the pause menu and the loading screens: titles and labels |
smlfnt2.fnt |
Small text in the same places: buttons, lists and OpenReliant's version |
itacbig.fnt |
The ITAC's large text, and the titles of the CD player and the simulator pod |
itacsml.fnt |
The ITAC's small text |
blufont.fnt |
The flight display's text: readouts, clock, windows and objectives |
ld_handel.fnt |
The loadout's tooltip, and the titles and ship names on its panels |
handels.fnt |
The figures on the loadout's info panel |
font_01.fnt |
The developers' text on the main menu |
- Layout. The game lays out text using the bitmap font's character widths, which don't change. Each character of the mod's font is drawn where the bitmap font would put it, centred on the inked part of the bitmap glyph, with capitals as tall as the bitmap font's, so a font with other proportions still fits each screen.
- Characters. The game's text uses Windows code page 1252. Characters the font doesn't have are drawn from the bitmap font.
- Colour. The flight display's font and the loadout tooltip's font are drawn through palettes. A mod's font for them is drawn in the colour of the bitmap font's letters, and glyphs the bitmap font draws in other colours, such as symbols, keep their bitmaps.
- The loadout's panels. The panels keep their bitmap text at the original's size. With a larger
fpanels.png(Pictures), their text is drawn from the outline fonts at the panels' size instead. - Weight and outline. A mod's font is drawn at its normal weight. Each character gets a black outline one bitmap pixel wide, like the text in the original.
- Built in. OpenReliant draws all text except the developers' text with Newtown, a public domain
font in the style of Handel Gothic, the original's typeface, with strokes as heavy as the bitmap
font's. A mod's font takes priority over Newtown, and a
.fntfile in a mod over both. - Licence. The font is distributed with the mod, so its licence must allow that.
A script can also write text in a mod's font, over any of the game's fonts (Pictures, shapes and fonts).
The loadout's panels, which draw their text into their own pictures, and the flight display's small fonts, used for target ranges and the radio menu, still use their bitmaps (#520).
Improvement: the original draws text with its bitmap fonts at 640x480.
The radio's window shows the face of whoever speaks, as a face film, while their line plays. A mod can replace the game's faces and lines, and give its own pilots faces and voices (Pilots). A script can also change which line is said, or drop it (Changing what the radio says).
A face film (.fm8, Face films) is a loop of frames, played at 15 frames a
second. Each pilot has a film for talking, one for laughing and one for dying. The game keeps them
in pilots\pilots.hog, and sltool hog ls pilots/pilots.hog lists their names.
The game's films are 120 by 100 pixels. A mod's film can be sharper, at any size in whole blocks of 4 by 4 pixels: the radio's window draws every film in the same place at the same size, so a film of 480 by 400 shows four times the detail. Improvement: the original draws a film at its own size.
A film with the name of one of the game's replaces it. The game plays the 45th's films under the
squadron's name: the 45th Volunteers' through mission 13, and the 45th Tigers' from mission 14,
or as a mission's flying_tigers says (Face films, Each mission of
the campaign). So replace both, such as 45Volntrs_Moose.fm8 and
45Tigers_Moose.fm8. examples/mods/trent gives Moose a new face this
way, and renames him from a load script (The records).
sltool fm8 encode <frames-dir> <film.fm8> makes a film of the PNG files in a folder, all the same
size, in the order of their names. A pixel less than half opaque becomes the colour the radio's
window draws see-through. The film keeps every colour where the frames have 256 or fewer,
and picks 256 for them otherwise, the see-through colour kept as it is.
sltool fm8 extract <film> <out-dir> saves a film's frames as PNG files, to start from.
The game keeps its lines in ms_speech\msspeech.hog, under names without an extension, such as
ABRT_001. A mod's line replaces the game's line with the same name. sltool speech extract ms_speech/msspeech.hog lines saves every line as a WAV file, to find the one to replace.
The best way to replace a line is with a recording: a WAV, FLAC, MP3, Ogg Vorbis or Opus file
named after the line. For example, abrt_001.wav or abrt_001.opus replaces ABRT_001. If a mod
has more than one, OpenReliant uses the first in the same order as for music (Music). The line plays as
recorded, at its own rate, mono or stereo, with the same effects as the game's lines: the radio's
sound for a radio line, and a briefing's last word at the narration's loudness. WAV files can hold
16-bit or 8-bit PCM or IMA ADPCM, which audio editors write by default. FLAC is about half the size
of WAV, MP3 about a fifth and Opus about a tenth: 64 kbit/s suits speech in MP3, and 32 kbit/s in
Opus. Enriquez's scenes in the induction and the news reports take recordings too, named after the
scene without .box, such as 0015.mp3 for 0015.box.
A line can also be a speech file in the game's own codec (Speech files),
named abrt_001.ut or abrt_001, which works in older versions of OpenReliant too. Where a mod has
both, the recording is used, and where it has both speech files, abrt_001. The codec suits short
radio lines like the game's own, but distorts long, clean speech, such as a spoken briefing.
sltool speech encode <line.wav> <line.ut> makes one from a WAV file, which it mixes to mono at
22,050 Hz, the rate the radio plays at:
sltool speech encode taunt.wav trptnt_001.utImprovement: the original plays lines in its own codec alone.
A ship's pilot says some lines in its own voice: the voice's prefix followed by the line, such as
bantnt_001 for Bandit's first taunt. The game's voices are ban, dic, fre, vip, enq,
sil, tak, jor, vix, cut, cla, ski, jui, fac, haw, arr, ner, rhi, sta,
fla, wor and ego for the pilots on the player's side, and rus, chn and arb for the
enemy's (Remarks).
| Lines | When the pilot says one |
|---|---|
tnt_001 to tnt_013 |
An enemy pilot taunts the player after hitting the player's ship |
dth_001 to dth_006 |
The pilot dies |
ejt_001 |
A wingman ejects |
res_001 to res_003 |
After a wingman ejects, the wing's last pilot calls in the rescue |
_amt_001 to _amt_008 |
A wingman answers ATTACK MY TARGET |
_bkoff_001 to _bkoff_009 |
A wingman answers BACK OFF |
_hlpme_001 to _hlpme_008 |
A wingman answers HELP ME |
So the voice trp has lines such as trptnt_001 and trp_amt_001. A pilot based on Bandit,
Diceman, Viper, Enriquez or Hawkeye answers the wingmen's commands from a fuller set of lines
(The wingmen's commands). A line the voice doesn't have
is left out, and the log says so.
A mod can add ship types, guns, missiles and pilots. Each one is based on one of the game's (a ship type can also have no base, see Ship types without a base):
- It starts with a copy of its base's stats, which a load script can change (The records).
- It behaves like its base wherever the game treats that base specially. A ship type based on the Phoenix carries the Nova Cannon, a gun based on the Nova Cannon charges up and strikes with its own damage, and a missile based on the Havoc sets off a shockwave.
The manifest lists each kind in a section of its own, and describes each entry in a section named after it:
[Guns]
banana_gun=
[Gun banana_gun]
Base=pulse_cannon
Name=Banana GunWhat all four have in common:
Baseis the game's record the new one is based on, by OpenReliant's name for it, such aspredatororpulse_cannon, or by its number. Guns, missiles and pilots need one.Nameis what the game calls it, such as on the flight display. Without it, it takes its base's name.- When it starts, OpenReliant numbers what the mods add after the game's records, mod by mod in
load order, so the numbers depend on which mods are on. Scripts therefore use names: the mod's
folder name or its archive's name without
.hog, a colon, and the name in the manifest, such asbananas:banana_gunfor the gunbanana_gunof the mod in the folderbananasor inbananas.hog. - The number after a name in the list is the number the mod's own files, such as its missions and models, use for it. When OpenReliant loads one of the mod's files, it replaces that number with the one it gave, so the files stay in the game's formats. Leave the number empty when the mod's files don't use it. The game's files can't use what a mod adds, and another mod's files can only through that mod's own list (Another mod's records).
- An entry with a mistake, such as a base that isn't one of the game's, is left out, and the log says why.
| Kind | List section | Its own section | First number | Most the mods add | Numbered in the mod's |
|---|---|---|---|---|---|
| Ship types | [ShipTypes] |
[ShipType name] |
256 | 732 | missions' ships, and the carriers they launch from |
| Guns | [Guns] |
[Gun name] |
16 | 240 | models' gun muzzles |
| Missiles | [Missiles] |
[Missile name] |
11 | 245 | models' missile hardpoints, for every loadout tier |
| Pilots | [Pilots] |
[Pilot name] |
194 | 61 | missions' ships' pilots |
examples/mods/interceptor adds a faster Predator under a name
of its own, examples/mods/bananas a gun, a missile, a pilot and a
ship that carries them, and examples/mods/teapot a ship with a model
of its own, made from an OBJ file (Models from OBJ).
A mod's files can use what another mod adds, if the mod requires that mod. It lists the record in its own list section by its qualified name, with the number its files use for it:
[Mod]
Requires=realistic-planets
[ShipTypes]
realistic-planets:mars=300- The mod's missions then place Realistic Planets' Mars as type 300. The record stays the other mod's, with everything that mod gives it, such as its planet entry, so it has no section here.
- The other mod must be in
Requires, which makes sure that it's on and loads first, and the entry needs a number. An entry that names a mod this mod doesn't require, or a record that mod doesn't add, is left out, and the log says why. - It works the same way for guns, missiles, pilots and game variables.
Improvement: the original's ship types, guns, missiles and pilots are its own.
[ShipTypes]
teapot=300
[ShipType teapot]
Base=predator
Model=teapot.shp
Schematic=teapotscem.spr
Name=Teapot
Guns=banana_gun
Missiles=banana
Cockpit=temg_frm.shp
WireFrame=teapotwire
WingIcon=teapoticon
EngineSound=kettle.wav- A ship type uses its base's cockpit, engine sound and display pictures, except for the ones it gives itself.
Modelis the type's model, a.shpfile in the mod or the game (.SHPmodels). It can be one of the game's models under the new type's own stats.Schematicis the sprite set the display shows the ship in, as the player's ship and as a target. Without it, the type shows its base's. Where the mod has no file of that name, the type takes the base's sprite set as a template, and the mod's pictures named after the schematic draw over its shapes (Shapes):teapotscem_000.pngis the ship, andteapotscem_001.pngtoteapotscem_004.pngthe hit markers of its four quadrants, which otherwise stay the base's.Gunsis the gun every gun of the model fires, andMissilesthe missile every hardpoint holds, whatever the model names: one of the game's by its name, one a mod adds by its qualified name, or one this mod adds by its own name.
A ship type based on one of the twelve ships the player can fly, or without a base, is offered on the loadout screen too, after the game's ships:
Tieris the campaign tier it's first offered at, 0 at the start to 3 after mission 21. Without it, it's offered from the start of the campaign.- Its name and its model are its own, drawn in green (see Textures in the loadout), and shown as large as its base whatever its model's size. The bars on its panel show its own stats, measured against the game's fighters.
Class,AccessandCrewset what the panel says about it.Classis one oflight,light_medium,medium,heavy,advanced_heavy,prototype_mediumandprototype_light,Accessone ofbronze,silver,goldandplatinum, andCrewa number. Without them, the panel shows its base's. Its specials and guns are its base's.GunsModelis the model the guns view shows in place of the ship, a.shpfile in the mod or the game, in the same units asModel. Make it as the game's are, such aspredator_gun.shp: the hull in lines, which only the ambient light reaches, and the guns as solid parts. Without it, the guns view shows its base's gun model.- The arc holds twelve ships. When the game's ships leave no room, the mods' ship types that don't fit aren't offered, and the log says so.
- A saved game keeps a mod's ship type as its base, so that the original can still load it. OpenReliant keeps the mod's own beside the save (Saved games), and puts it back when the save is loaded with the mod still on.
When the player flies it, it can also give:
Cockpit, the model of the cockpit's frame, a.shpfile in the mod or the game, such as the Tempest'stemg_frm.shp.WireFrame, the name of the pictures the gunnery display shows the ship as, in place of its base's wire frame:teapotwire_000.pngis the ship, andteapotwire_001.pngon the ship with its first, second and later group of guns lit, which the display shows over it for a ship of more than one group. Each is drawn over the base's wire frame's rectangle, so make them all the same size, in the shape of the base's: the Predator's is 92 by 108 of the display's pixels, and a picture at two or four times that keeps it sharp. A picture left out keeps the base's shape.WingIcon, the name of the picture the wing's window shows the ship as,teapoticon_000.png, drawn over the base's icon's rectangle.EngineSound, a WAV file, PCM or IMA ADPCM at any rate, that loops as the engine's sound. The throttle changes its pitch and volume as it does for the base type's sound. Give it whole cycles of each tone, so that its loop doesn't click.BlindFireandSpectralShields,yesorno, whether it carries blind fire and spectral shields. Without them, it carries what its base carries. The loadout lists them with its specials.
A mission can put the player in a ship type based on a ship the player can't fly, such as the
Basilisk. The player flies it with a cockpit only if it gives Cockpit. Its base has no wire frame,
wing icon, blind fire or spectral shields, so it has only those it gives, and its WireFrame and
WingIcon pictures are drawn over the Predator's rectangles, as for a type without a base. Without
a Cockpit, the player flies it like the game's own ships of that kind, without a cockpit, a wire
frame or a wing icon, and the log names the keys it leaves unused.
A mission can launch ships from a mod's ship type, the player's among them. One that keeps its
base's model launches them as its base does, such as from the Reliant's tubes. One with a model of
its own launches them as a hangar bay: each ship waits at the launch point of its gate, a
launch_point object of the model (Models from OBJ), and flies out once its
launch starts.
The player's ship lands only on a ship that offers a landing. A type's Landing says how the
player's ship lands on it:
bay: the ship flies in at the doors of a gate and down to its launch point, as into a hangar bay. The model needslaunch_pointobjects (Models from OBJ).reliant: the Reliant's landing, down its first launch tube, which needs the Reliant's model.yamato: the Yamato's landing, onto a pad of the Yamato's landing bay.badanov: the Badanov's landing, into the bay of the Badanov's or the Krasny's model.none: no landing.
Where the model lacks what its landing needs, nothing lands on it, and the log says so.
Without the key, a type that keeps its base's model offers its base's landing, so a type based on the Reliant lands the player as the Reliant does. A type with a model of its own offers none.
Where the player's carrier offers a landing, PERMISSION TO LAND brings the player's ship back into
it once the mission's script has cleared it to land (game.landing_cleared = 1): the carrier's
bridge answers, and the ship lands, which ends the mission. The Yamato's bridge officer answers for
a Yamato and the Reliant's for any other carrier, a mod's too
(#1340). In a bay, the ship flies in at
gate 0. A mission can land the ship at another gate, and at a moment of its own, with
SetAI(player, "land", 1, component(carrier, gate))
(Landing). A mission can also let the player land on a ship that
offers no landing, such as the Victorious, or keep the player off one that does
(Landing on another carrier).
A ship type can leave out Base. It then behaves like no ship the game treats specially:
- Its stats start as a copy of the Predator's, which a load script can change (The records). The loadout shows it at the Predator's size.
- It carries blind fire or spectral shields only if
BlindFireorSpectralShieldssays so. - On the loadout's panel, it is
light,bronzeand has a crew of 1, unlessClass,AccessandCrewsay otherwise. - The loadout reads its guns and specials from its model: each kind of gun its gun muzzles fire,
or the gun
Gunsnames, up to four kinds; the Nova Cannon if it fires one; the cloaking device if the model can cloak; and reverse thrust if an engine glow points forward. Without aGunsModel, the guns view shows its own model, all in red. - Its cockpit, engine sound and display pictures are the Predator's unless it gives its own.
- A saved game keeps it as the Predator.
[Guns]
banana_gun=
[Gun banana_gun]
Base=pulse_cannon
Name=Banana Gun
Shot=banana_shot.png
ShotSize=40
Sound=boing.wav
Flash=banana_shot.png
FlashSize=300A gun uses its base's shots, flashes and sounds, except for the ones it gives itself:
Shotis a picture in the mod, which each shot is drawn as: one flare of it, facing the camera, fading with the shot's life. It is found as the mods' textures are, by its name without the extension, so a PNG, DDS or KTX2 file works. Give it a transparent background.ShotSizeis how far the picture reaches from the shot's middle in each direction. The default is 60, the size of the Pulse Cannon's flare.Soundis a WAV file in the mod, PCM or IMA ADPCM at any rate, that each shot makes instead of the base gun's sound. It is heard like the base gun's sound: as far, as loud, and following the shot.Flashis a picture in the mod, which the muzzle flash is drawn with as each shot leaves the gun, in place of its base's flares: across the muzzle and in three blades along the flash, added to what's behind it, shrinking away as its base's does. It is found asShotis. Black is see-through, so draw it on black. Where flashes light the ship, its light takes the picture's colour.FlashSizeis how far the flash reaches forward of the muzzle, its width keeping its base's proportions. The default is its base's flash, 600 for the guns based on the fighters' guns.
A gun whose Sound is missing from the mod, or isn't a WAV file, is left out, and the log says so.
A model gives each gun muzzle a gun type by number, so a mod's own model fires the mod's guns by the
numbers in [Guns]. A ship type can also fire one with Guns, whatever its model says.
[Missiles]
banana=
[Missile banana]
Base=bandit
Name=Banana
Model=banana.shp
Description=A homing banana. It locks on slowly, but nothing outruns it.
Tier=0A missile uses its base's trail, sounds and flight display picture. What Model is depends on the
base:
- For a base that hangs on a rail (the Havoc, the Jack Hammer, the Bandit, the Vagabond and the
Imp),
Modelis the missile that hangs on the hardpoint and launches from it. - For a base that hangs in a pod (the Screamer, the Raptor, the Solomon and the Hawk),
Modelis the missile that flies out of the pod, andPodis the pod that hangs on the hardpoint. WithoutPod, the base's pod is used.
A mod's own model can hold the missile on its hardpoints, and a ship type can carry it with
Missiles.
The loadout screen offers it on its missile page too, with the game's missiles:
Tieris the campaign tier it's first offered at, 0 at the start to 3 after mission 21, else its base's. A missile based on the torpedo, which the loadout never offers, isn't offered.Descriptionis the text on its panel. Without it, the panel shows its base's text. The panel's figures are its own, measured against the game's missiles, and a ship carries as many of it as of its base.- Its icon on the arc is the model that hangs on the hardpoint:
Model, orPodfor a base that hangs in a pod, else its base's loadout model. Its red copy is made from its picture (see Textures in the loadout). - The arc has twelve places, and the game's missiles take five to ten of them by the tier. The mods' missiles take the free places in the order the mods load; the log says when some don't fit.
- A saved game keeps a mod's missile on a rack as its base, so that the original can still load it. OpenReliant keeps the mod's own beside the save, and puts it back when the save is loaded with the mod still on.
[Pilots]
trooper=
[Pilot trooper]
Base=21
Name=Trooper
Voice=rusA pilot uses its base's face and voice on the radio, except for the ones it gives itself. Base is
a pilot's number in pilotstats.bin. A mission gives its ships' pilots by number, so a mod's own
missions use the mod's pilots by the numbers in [Pilots]. A script can set the pilot of any ship
(Objects), as the bananas example does.
Talking,LaughingandDyingname the face films the radio's window plays as the pilot speaks, laughs and dies: one of the game's, such as45volntrs_pltfrompilots.hog, or a.fm8file in the mod, with or without its extension (Faces). A fourth film, the 45th's pilot, is the same for every pilot and can't be changed.Voiceis the prefix of the names of the pilot's lines (A pilot's voice). The pilot uses it on either side, in place of its base's two voices. It can be one of the game's, such asbanfor Bandit's orrusfor the Coalition's, or a new one, whose lines the mod gives.
A mission's script keeps the state of the campaign in the game's variables, such as whether the
Krasnaya got away (krasnaya_alive), and the campaign keeps them from one mission to the next
(The game's variables). A mod can add game variables
of its own, such as a flag that says whether the player beat the pirates in the mod's first
mission. The manifest lists them as it lists ship types and guns
(New ships, guns, missiles and pilots), but they have no
Base and no Name:
[GameVariables]
pirates_beaten=40
alarm=41
[GameVariable pirates_beaten]
Start=1
[GameVariable alarm]
Attempt=0
Saved=no- The number after a name is the number the mod's missions use for it, from 38 to 255, as in
game[40]in mission source. Numbers 38 to 63 are spare in the original's block of 64, so a mission that uses only those also runs in the original, which doesn't keep them with its saved games. OpenReliant numbers the mods' variables from 64 on, and changes the numbers in a mod's missions as it loads them, so two mods never clash. Leave the number empty for a variable that only scripts use. Startis the value a new campaign gives it, 0 without it.Attemptis the value each attempt at a mission starts it at, as the game's ownobjectives_metstarts at 0. Without it, the campaign keeps the variable from one mission to the next.Savedsays whether a saved game keeps it,yeswithout it. A saved game keeps a mod's variables in its.modsfile, by their qualified names, such aspirates:pirates_beaten. Loading a saved game from before the mod was on gives a variable itsStart.- These keys make the variable's record in
records.game_variables, beside the records of the game's own variables. Load scripts can change any of them (The game's variables). - Scripts read and set the values with
world.variableandworld.set_variable, a mod's by its qualified name, or by its own name in the mod's own scripts (Objects). The news reports after a chapter's movie can wait on them and set them too (chapter_reports, Each mission of the campaign). - Another mod's missions can use them through that mod's own list (Another mod's records).
Improvement: the original's game variables are its own, and its code holds what the campaign does with them.
A model is a .shp file (.SHP models). sltool builds one for a mod from an
OBJ or a glTF file, and exports the game's models as glTF to start from.
sltool shp from-obj builds a model for a mod from a Wavefront OBJ file, which Blender and most
modelling tools export, with nothing of the game's in it:
sltool shp from-obj teapot.obj teapot.shp --two-sidedEach object in the file becomes a part of the model according to its name, written in any case. A
suffix such as .001, which modelling tools add to copies, is ignored:
| Object | What it becomes |
|---|---|
cockpit |
The cockpit, a part of its own, which leaves the ship as the pilot's pod when the pilot ejects |
gun_muzzle:<gun type> |
Where a gun fires from, a gun of that type, by its number in gunstats.bin (1, the Laser Cannon, without one) |
missile:<missile> |
A missile hardpoint, holding that missile type for every loadout tier (0 without one) |
engine_glow:<glow> |
An engine's glow, burning backward from the middle of its box: the box's width and height are the plume's, half its length how far the plume reaches at full throttle. The number picks one of the game's seven glows; the player's ships use 1 |
light:<colour> |
A light: a sprite as large as its box, which lights nothing round it |
eject_point |
Where the pilot's pod is thrown up from, on the cockpit where there is one |
launch_point, dock_point |
Where a ship launches from or docks |
jump_trail |
Where one of the trails streams from when the ship jumps |
jump_light |
Where one of the lights flashes along the hull when the ship jumps |
| anything else | The body |
An attachment sits at the middle of its object's corners, and is as large as their bounding box, so a small box or a single triangle is enough to mark one. In the teapot's OBJ file, each gun muzzle, engine glow and missile hardpoint is one triangle:
o Teapot
usemtl teapot
f 1/1/1 2/2/2 7/7/7
...
o cockpit
usemtl teapot
f 501/501/501 507/507/507 506/506/506
...
o gun_muzzle:1
f 801 802 803
o engine_glow:0
f 807 808 809
o missile:0
f 813 814 815
o eject_point
f 825 826 827
- Textures. Each face of the body and the cockpit takes the texture its
usemtlnames, by the texture's name without its extension, such asteapotforteapot.pngin the mod. A face without ausemtlis drawn untextured. The.mtlfile isn't read. - Normals. A vertex uses the normal the file gives it. Without one, it gets the average of the normals of the faces around it.
- Options.
--two-sideddraws every face from behind as well, for a model with open edges.--cloakadds the meshes a ship needs to cloak.--densitysets how heavy each part is for its size: the default, 0.1, makes a ship about as heavy as the Predator at the Predator's size. - Axes. The file is read as Blender exports it, with Y up and the nose toward +Z.
sltool shp objexports the game's models the same way, so a game model exported and built again comes back in the same place. - Size. A part holds at most 65,535 vertices and 65,535 triangles, the format's limit, and
sltoolstops with an error past it. The game's fighters have a few hundred triangles; OpenReliant draws tens of thousands, but each one costs time in every frame, shadows included. - What is generated. Each part gets one level of detail, a collision tree of boxes around its
faces, and a mass as though it filled its bounding box. A model without
jump_trailobjects gets a jump trail at each engine glow, so that each engine streams one when the ship jumps.
examples/mods/teapot builds its ship this way, and
examples/mods/bananas its missile.
sltool shp from-gltf builds a model for a mod from a glTF 2.0 file, the format Blender and most
modelling tools export and most model sites offer. It reads a .gltf file with its buffers in files
beside it or inside it, or a binary .glb file:
sltool shp from-gltf viper.gltf mods/viper/viper.shp --scale 2The model is built as from an OBJ file (Models from OBJ), with the same names and options:
- Nodes. Each node with a mesh becomes an object of its name, and each node without a mesh or
children, such as Blender's empty, marks an attachment or a jump point by its name, such as
gun_muzzle:1. A marker is a cube two units across, scaled, turned and moved as its node is, so scale an engine glow's marker along its length to give its plume that length. Every node's transform is applied, and--scalescales the whole model. - Materials. Each material becomes a texture named after the model and its number, such as
viper_0.pngforviper.shp, written next to the model with its maps (Material maps): the colour, which is the colour texture multiplied by the colour if there is one; the roughness and metalness, from the textures and values; the normal map; and the emissive map if the material glows,KHR_materials_emissive_strengthincluded. Every map has the size of the colour texture. Textures must be PNG files. A texture that isn't a PNG file, or whose file is missing or can't be read, is skipped with a warning. - What isn't read. Animations, skins, morph targets, cameras, lights, sparse accessors, a second
set of texture coordinates, and the extensions beyond the emissive strength. A file that requires
an extension other than
KHR_materials_emissive_strength,KHR_materials_specularorKHR_texture_transform, which it can be drawn without, isn't read.
To start from one of the game's models, sltool shp gltf writes it as glTF, its attachments as
empty nodes named as from-gltf reads them, and with --textures its pictures beside it:
sltool shp gltf USLF_Prd.SHP predator.gltf --textures tcachehw.dat palette.tgaEach part is a node under the part it hangs from, with its first level of detail (--lod picks
another). Without --textures, the materials name their pictures, but the pictures aren't written.
When you build the model again with from-gltf, every part except one named cockpit joins the
body, the textures are renamed after the new model, and missing pictures are skipped. Share only
your own work in a mod, never the game's models or pictures.
A mission is a .DTE file (.DTE missions) named after its number, such as
mission5.dte. The game keeps its missions in missions\ and resource.hog. A mod's
mission5.dte replaces mission 5, and a mission with a number of its own, such as mission90.dte,
adds one, which a game mode can fly (Game modes) and --mission 90
starts. A mission numbered from 1 to 28 can also join the game's campaign, such as the missions 12,
13, 17 and 22 that the campaign doesn't fly (The campaign's
missions). A load script gives it what the campaign gives its
missions: the briefing, the carrier, the objectives, the date, the awards, Enriquez's report and
debriefing, the ITAC's news, the landing and the wing's pilots (Each mission of the
campaign).
A mod that puts mission 12 back has mission12.dte, a recording of its spoken briefing,
brief12.mp3, and a load script:
[Mod]
Name=Mission 12 Restored
Version=1.0
[Scripts]
Load=campaign.luau-- campaign.luau
local records = require("openreliant.records")
-- Fly mission 12 between missions 11 and 14.
local campaign = records.campaign
table.insert(campaign, 12)
table.sort(campaign)
records.campaign = campaign
-- Enriquez speaks the briefing over another mission's movie, and debriefs it.
local mission = records.missions[12]
mission.hologram = "new_m05.bik"
mission.speech = "brief12.ut"
mission.objectives = { "Protect the Reliant", "Destroy the Black Guard" }
local debriefing = mission.debriefing
debriefing.success = { "Good work out there. The Reliant is safe, for now." }
mission.debriefing = debriefing- OpenReliant reads a mission as the original does, and has no mission format of its own. A mission made for the original works in OpenReliant, and one made for OpenReliant works in the original unless it uses what a mod adds.
- A mod's mission can use the ship types, guns, missiles and pilots the mod adds, by the numbers in its manifest (New ships, guns, missiles and pilots).
- A script can run with a mission: the manifest lists it under
[Missions](Kinds of scripts). sltool dtelists a mission's ships, triggers and script parts, and disassembles its script (Tools).- OpenReliant Editor, a mission editor and
script debugger, is out as an early preview
(#1126). A mission can also be written
as Luau source in any text editor, which
sltool dte buildcompiles, andsltool dte decompilewrites any mission as source (Writing missions). StarLancerEditor, a community project, turns missions into YAML files and back.
A mission a mod adds or replaces can be started straight from the command line, and checked at any moment of it without flying up to that moment (Configuration):
openreliant --mission 2 --skip-launch --part "antanov in" --screenshot antanov.png --screenshot-ticks 1000
openreliant --mission 2 --skip-launch --watch reliant --screenshot reliant.png --screenshot-ticks 300--mission <number>starts the mission, from a mod or the game.--skip-launchplays the player's launch through without showing it.--part <name>runs a part of the mission's script once the launch is over, as a trigger would.sltool dte partslists the parts. A part starts from the mission's opening state, so one that counts on ships or flags from an earlier part may find them missing.--watch <ship>points the camera at one of the mission's ships, and--watch-fromsays from where.sltool dte shipslists the ships. A ship's whole name picks it, though other ships' names hold it too, such asRELIANTbesideRELIANT NANNY. A cutscene in the mission's script takes the camera from it.--screenshotsaves a picture after--screenshot-ticksgame ticks and quits.
openreliant missions lists and checks the missions, the mods' included, and shows mod in the
file column for a mod's mission. It also takes --no-mods.
Mods can include scripts written in Luau, a version of Lua, which OpenReliant runs while you play. Scripting explains them step by step, starting from a first script, and the scripting reference lists everything they can use. In short:
- Load scripts run once at startup and change the game's records: the stats of ships, guns, missiles and pilots, and the game's text.
- Global scripts run for the whole game, and mission scripts while their mission runs. They react to what happens in the game, and change it, through hooks on the game's functions and events.
- Object scripts run on each ship of a class or a type, such as every fighter, while it's in the mission, missile scripts on each missile in flight, and turret scripts on each turret.
- Player and menu scripts draw over the flight display and the menus, play sounds and music, and react to the keys. Menu scripts can also replace the front end's screens, and add game modes and campaigns to the main menu's GAME MODES (Menus, game modes and campaigns).
A mod lists its scripts in its manifest:
[Scripts]
Load=balance.luau
Global=rules.luau
[Missions]
mission2.dte=escort.luauScripts are ordinary files of the mod: a folder mod keeps them next to mod.ini, and sltool hog pack packs them into the archive. The shaders of a mod's post effects and functions, files
ending in .frag or .glsl, are kept the same way (Post effects).
Neither scripts nor shaders replace game files, but a mod's device.glsl, bloom.glsl or
shadow.glsl can replace OpenReliant's own shader
(Replacing OpenReliant's shaders).
examples/mods holds example mods for mod makers, which show how the
scripting works and aren't supported mods.
Improvement: the original has no scripting apart from its mission scripts.
sltool comes with openreliant in each release
(Builds and releases). The commands a mod maker uses
most:
| To | Run |
|---|---|
| List or extract an archive's files | sltool hog ls <archive>, sltool hog extract <archive> <out-dir> |
| Pack a folder mod into an archive | sltool hog pack <folder> <archive> --checksum |
| Save the game's textures as PNG files | sltool tcache extract tcachehw.dat palette.tga <out-dir> [name...] |
| Save a sprite set's shapes as PNG files | sltool spr extract <sprite> <out-dir> |
| Export one of the game's models | sltool shp gltf <model> <out.gltf>, sltool shp obj <model> <out.obj> |
| Build a model | sltool shp from-obj <in.obj> <out.shp>, sltool shp from-gltf <in.gltf> <out.shp> |
| Save a face film's frames, or make a film | sltool fm8 extract <film> <out-dir>, sltool fm8 encode <frames-dir> <film> |
| Save lines as WAV files, or make a line | sltool speech extract <archive> <out-dir>, sltool speech encode <in.wav> <out.ut> |
| List a sound bank's sounds, or save them as WAV files with the names a mod's sounds use | sltool fat ls <bank>, sltool fat extract <bank> <out-dir> |
| List a mission's ships, triggers and script parts | sltool dte ships <mission>, sltool dte triggers <mission>, sltool dte parts <mission> |
| List a stats table | sltool stats list <stats.bin> |
sltool help lists every command, and the README
links each one to its format's page. openreliant has three commands for mod makers too:
openreliant missions checks missions (Checking a mission),
openreliant hooks lists the hooks scripts can use (Hooks), and
openreliant debug steps through a mission's script as the game plays it
(Debugging mission scripts).
A folder mod is handy while you work on it. To share it, pack it into one archive, which players
put in their mods folder:
sltool hog pack mods/coyote coyote.hog --checksumThe archive holds every file of the folder, the manifest, the thumbnail, the scripts and the shaders
included, and works just as the folder did. --checksum writes coyote.hog.sha256 beside it
(Checksums).
- Give the manifest a
Name, aVersion, aDescriptionand aUrl, so that players know what the mod is and where to find its updates, andOpenReliantfor the version it needs (The manifest). - Share only work you may share: your own, or work whose licence lets you share it. Fonts, models, pictures and sounds from elsewhere each have a licence of their own.
- OpenReliant's mods page lists mods made for OpenReliant, and the mods screen's GET MODS installs them from it (Getting mods from the catalogue).
A mod can include a picture of itself, mod.png, which the mods screen shows above the mod's
details. It fits in a box 175 by 70 of the screen's 640x480 points, so a wide picture such as
700x280 fills it best. Like the manifest, it doesn't replace any game file.
An archive can have a checksum file next to it: the archive's name plus .sha256, in the format
sha256sum writes (the archive's SHA-256 hash in hexadecimal, two spaces, and the archive's name).
When the mod loads, OpenReliant checks the archive against it, and skips the mod if they don't
match, since the archive is then damaged or isn't the one the checksum was made for. The mods screen
lists it in red. sltool hog pack <folder> <archive> --checksum writes a checksum file next to the archive it makes, and
sha256sum -c music.hog.sha256 checks one by hand. Folder mods don't have checksums.
OpenReliant writes its log to the terminal it runs in, and to openreliant.log in the game folder
(The log file). Attach it to a mod or scripting
problem when you
report one. As it starts, it lists each mod it loads, in order, by its manifest's name if it has
one, and then what each of the mod's files does: which game file, texture, shape, picture, font,
line or sound it replaces, which earlier mod's file it replaces, or which file it adds. A recording
in a different format from the game file it replaces, such as New_Pensive.flac, is listed as
replacing New_Pensive.wav, and a movie such as warty_.webm as replacing the movie
warty_.bik. A mod that the mods screen has turned off is listed as off, and none of it is used.
When a font is loaded, the log says which outline font draws it.
info(mods): music.hog matches music.hog.sha256
info(mods): mod 1 of 2: Coyote HD 1.0, by Someone (coyote)
info(mods): coyote replaces USA_Coyote.SHP
info(mods): mod 2 of 2: music.hog
info(mods): music.hog replaces New_Pensive.wav
info(mods): music.hog adds msc_theme.wav
info(mods): the mod old-ships is off
info(fonts): optfnt.fnt uses Newtown, with strokes 0.002 em wider
info(scripts): balance: ran balance.luau
info(scripts): balance: the Laser Cannon hits shields for 10 and hulls for 10
A script's messages and errors follow its mod's name, as above; Scripting explains the common ones.
| In the log | What to check |
|---|---|
A file adds where it should replace one of the game's |
Its name: the game file's name, without folders, in any case. Textures, pictures, shapes, lines, music, sounds and faces are named as their sections say. If the log also says can't list the game's ..., the name may be right |
can't list the game's archive <name>, so the log may say a mod adds a file it replaces |
The game's archive is damaged. The mod still works if its files are named right |
skipping <name>: <error> |
A link in mods or in a mod whose target is missing, or an entry that can't be read: fix or remove it |
skipping <name>: a mod must be a .hog archive or a folder |
Only folders and .hog archives go directly in mods |
skipping <mod>/<file>: a mod's files must be directly in its folder |
Move the file out of its subfolder |
skipping <mod>/<file>: file names must be plain names in printable ASCII, like archive member names |
Rename the file |
skipping the mod <mod>: it needs OpenReliant <version>, and this is <version> |
Update OpenReliant, or check the mod's OpenReliant= (The manifest) |
skipping the mod <mod>: it needs the mod <name>, which doesn't load before it |
Add that mod, turn it on, or move it above this one on the mods screen (The manifest) |
skipping the mod <archive>: checking <file> failed: ... |
The archive doesn't match its checksum: download it again, or pack it again with --checksum (Checksums) |
<mod>: mod.ini: the <kind> '<name>' ... |
The entry the message names, and what it says is wrong (New ships, guns, missiles and pilots, Game variables) |
skipping the picture that replaces <name>: it's <size>, and the original is <size> |
That picture must keep the original's size (Pictures) |
skipping the picture that replaces <name>: it's <size>, and it must be a whole multiple of the original's <size>, at most 8192 pixels across |
That picture must be the original's size or a whole multiple of it (Pictures) |
<set>: shape <n> is cut short: its rows run past the end of its block, so it draws nothing |
The set's file is broken at that shape: make the set again, or give a picture in its place (Shapes) |
the <map> of <texture> is left out: it is <size> and its picture <size> |
A map must be the same size as its texture (Material maps) |
the <map> of <texture> is left out: it has <n> mipmap levels and its picture <n> |
Save the DDS or KTX2 map with all its mipmaps, as many as the picture has (Compression) |
<file> is left out: it is compressed in <format>, which the device doesn't take |
The GPU doesn't take that format; use a PNG file instead (Compression) |
the line <name> is not in ms_speech/msspeech.hog |
The line's name (Lines) |
the radio's film <name> is left out: ... |
The film's name (Faces) |
the dead channel's film pilots\static.fm8 is left out too: ... |
The game's pilots.hog is missing or damaged |
the mod's music <path> may loop back to the wrong moment, since the game's piece can't be read: ... |
Keep the game's piece in the game folder (Music) |
the movie <name> is left out: its video isn't VP9 of profile 0, 8 bits with 4:2:0 colour |
Encode it again as VP9, with -pix_fmt yuv420p in FFmpeg (Movies) |
the movie <name> is left out: ... |
The file is damaged, or isn't a WebM or Matroska file of the kind OpenReliant plays (Movies) |
the movie <name> plays without its sound: it is <codec>, and OpenReliant plays Opus and Vorbis |
Encode its sound as Opus or Vorbis, with -c:a libopus in FFmpeg (Movies) |