Skip to content

# Manifest

## mod.toml

`id` and `version` are required. Every other key is optional.

    [mod]
    id      = "my-mod"        # the directory or zip name; enabled.txt and --mods use that name
    name    = "My Mod"        # shown in the Mods section; defaults to id
    version = "0.1.0"
    api     = 1               # minimum OPENPETE_MOD_API_VERSION; omit for a mod with no src/
    deps    = ["other-mod"]   # hard dependencies: their entry points run first
    sim_mutative = false      # see below
    state    = "none"         # "none" | "rebuildable": permits rewind with this mod loaded
    runahead = "pure"         # "pure" | "host-fx": permits runahead with this mod loaded

`deps`. Dependencies are topologically sorted ahead of enabled-list order, so a library's entry point runs before its clients' regardless of where each sits in `enabled.txt`. A cycle or a missing dependency is logged and disables the dependent.

`sim_mutative`. Whether the mod writes guest RAM. The engine tags a memory card with the mutative mods loaded at its first write and warns the player before playing that card under a different set; progress is never blocked, and `--adopt-saves` re-tags. A mod with no `src/` and no `[[level]]` is derived non-mutative. A mod with `src/` is assumed mutative unless it sets `sim_mutative = false`. Asset overlays, shaders, `draw_text`, `ui_status`, and `notify` never write guest RAM.

`state`, `runahead`. Both default to undeclared. An enabled code mod with `runahead` undeclared disables runahead; with `state` undeclared it refuses rewind and timeline jumps, with a one-shot warning. `runahead = "host-fx"` declares tick-hook side effects outside guest RAM (sounds, files), which the engine suppresses during speculative ticks. `state = "rebuildable"` declares that every host-side value derived from guest history can be re-derived from guest RAM after a jump.

### `[[binding]]`

    [[binding]]
    name    = "warp"          # read with api->binding_down(self, "warp")
    key     = "F6"            # default key
    purpose = "warp home"     # shown to the player

Up to 8 rows. Conflicts across the enabled set are reported at load. Players rebind under `[keys.mod.<id>]` in `openpete.toml` with keyboard names, gamepad names such as `pad:north`, or `none`.

### `[[config]]`

Rows declare the mod's settings. The engine renders them in the M overlay's Mods section as checkboxes, sliders, inputs, and combo boxes, grouped and tooltipped. Apply writes the edit into `config.toml` with comments preserved and reloads the mod at the next tick boundary, so the entry point re-reads the values through `api->config_*`.

    [[config]]
    key     = "gems.sfx"      # config.toml path this row edits
    type    = "bool"          # bool | int | float | string
    default = true            # shown when config.toml has no value
    label   = "S3 collect chime"
    group   = "Gems"          # section header within the mod's block
    help    = "tooltip text"

    [[config]]
    key     = "spawn.scale"
    type    = "float"
    default = 1.0
    min     = 0.25            # min < max renders a slider; omit for an input box
    max     = 4.0

    [[config]]
    key     = "render.mode"
    type    = "string"
    default = "fast"
    items   = ["fast", "fancy"]   # string + items renders a combo

Up to 32 rows. A mod whose UI is settings plus `api->ui_status` lines needs no widget code. Custom panels use `openpete_mod_ui.h`, which is versioned separately and carries no stability promise.

### `[[level]]` and `[[material]]`

See Custom levels and Shaders.

## config.toml

Optional, beside `mod.toml`. The engine parses it once, immediately before the entry point runs. Keys are dotted paths: `"gems.models"` is key `models` in table `[gems]`; a bare `"verbose"` is a root key.

    [gems]
    models = true
    sfx    = true

    [chests]
    collision = true

    int     on    = api->config_bool (self, "gems.models", 1);
    int64_t count = api->config_int  (self, "spawn.count", 4);
    double  scale = api->config_float(self, "spawn.scale", 1.0);
    char who[64];   api->config_str  (self, "greet.name", "world", who, sizeof who);

A missing file, a missing key, or a type mismatch returns the default passed in. A parse error is logged and every getter returns its default; the mod stays enabled. Bools read as 0/1 through `config_int` and `config_float`; ints read through `config_float`; floats truncate through `config_int`; strings never coerce.

Read the config in the entry point. Under `OPENPETE_MOD_HOT_RELOAD=1` an edit to `config.toml` reloads the mod against the cached object, so the entry point re-runs with the new values without a recompile.

## enabled.txt and --mods

See Enabling mods.