Courage.Assets#
require("Courage.Assets") maps files in your mod's assets/ folder to logical names used by the shader loader, Courage.Shaders, and CReact image / sprite primitives.
The game also exposes the same table on require("Courage.Game").assets.
Active mod context#
While Petrichor loads your mod (entry file and new() / init), the host sets the active mod folder and id. Courage.Assets only works during that time. Calling it from tick or a deferred callback raises an error.
API#
Courage.Assets is a callable table:
| Form | Returns | Description |
|---|---|---|
Assets(rel) | string | Logical name for a file under assets/ (strips .hlsl / .vs.hlsl, prefixes mod id) |
Assets.path(rel) | string | Absolute filesystem path: <mod-dir>/assets/<rel> |
Assets.mod_path() | string | Absolute filesystem path of the mod folder: <mod-dir> |
Assets.set_override(virtualPath, realPath) | boolean | Map a virtual archive path to a real file on disk. Returns true on success, false if the file doesn't exist. |
Assets.clear_override(virtualPath) | boolean | Remove a previously set override. Returns true if an entry was removed. |
Logical name rules#
- Slashes are normalized, and leading
./and/are stripped. - Shader extensions
.hlsland.vs.hlslare removed from the logical name. - Result is
<mod-id>/<path/stem>unless the mod id is empty (then just the path).
Examples for mod id shader_demo:
| Call | Result |
|---|---|
Assets("red_tint.hlsl") | "shader_demo/red_tint" |
Assets("filters/crt.hlsl") | "shader_demo/filters/crt" |
Assets.path("red_tint.hlsl") | ".../mods/shader_demo/assets/red_tint.hlsl" |
Use logical names with Courage.Shaders.set_enabled and list(). See Courage.Shaders.
Asset overrides#
There are two ways to redirect a game archive file to a mod file.
1. Filesystem overrides (passive, no Luau needed)#
Place a replacement file under mods/<mod-id>/assets/overrides/<archive>/<subdir>/<file>. The game checks every enabled mod folder in load-order on each resource fetch.
mods/my-mod/
└── assets/
└── overrides/
└── d_a_wolf/ (archive name, lowercase)
└── body.bmd (file to replace)
2. Assets.set_override (runtime, from Luau)#
Assets.set_override is checked before the filesystem scan. Use it for dynamic redirects: different files depending on mod state, save data, or player choice.
virtualPath format: <archive>/<subdir>/<filename>, all lowercased, using forward slashes. This matches the key the engine builds internally from the archive name and the entry path.
realPath can be: - An absolute path: used as-is. - A relative path: resolved against the mods/ directory first, then the current working directory.
The function validates that the file exists before storing the mapping. It returns false (and logs a warning) if the file cannot be found.
Both set_override and clear_override automatically invalidate the mod resource cache for the affected virtual path, so the change takes effect on the next resource fetch without restarting.
local Assets = require("Courage.Assets")
local MyMod = mod("my-mod")
function MyMod:init()
-- Redirect a specific archive file to a replacement on disk
local ok = Assets.set_override(
"d_a_wolf/body.bmd", -- virtual path (archive/file, lowercase)
Assets.path("replacements/hero_wolf.bmd") -- absolute path via Assets.path
)
if not ok then
print("override failed: file not found")
end
end
function MyMod:shutdown()
Assets.clear_override("d_a_wolf/body.bmd")
end
return MyMod
import * as Assets from "Courage.Assets"
declare function mod(name: string): any
const MyMod = mod("my-mod")
MyMod.init = function () {
const ok = Assets.set_override(
"d_a_wolf/body.bmd",
Assets.path("replacements/hero_wolf.bmd")
)
if (!ok) {
print("override failed: file not found")
}
}
MyMod.shutdown = function () {
Assets.clear_override("d_a_wolf/body.bmd")
}
export = MyMod
[!TIP] Call
Assets.clear_overrideinshutdownso overrides don't persist if the mod is disabled.
CReact textures#
CReact resolves asset / src on image, nineslice, and sprite through the engine asset registry:
| Source | Lookup |
|---|---|
| Memory assets | RGBA pixels registered at runtime (for example by Courage.Layout capture / update) |
| Mod files | mods/<mod-id>/assets/<path> when the key looks like <mod-id>/<path> |
| UI root | Paths relative to the packaged CReact UI assets folder |
Supported on-disk formats for mod files: PNG, JPEG, and BMP (decoded with stb_image). Retail TIMG textures inside loaded BLO screens are decoded when you rasterize through Courage.Layout.
Using assets in a panel#
Panel (creact/panel.tsx or popup.tsx)
Place icons/heart.png under mods/my-mod/assets/. The string from Assets(...) must match the key CReact looks up.
Example#
Asset layout#
mods/my-mod/
├── mod.json
├── main.luau
└── assets/
├── red_tint.hlsl
├── icons/
│ └── heart.png
└── filters/
└── crt.hlsl
HLSL files under assets/ are discovered automatically on load and after Shaders.reload(). Image files are loaded on demand when a CReact panel references their logical asset key.
For vanilla archive and file names when planning asset overrides, see the Archives tab of the TP Ultimate Spreadsheet.