Skip to Content
TutorialsA first code mod

A first code mod

A C mod that halves the blood, turns off the red hit flash and logs every level change. It shows the whole code-mod loop: write, build, install, read the log.

Code mods need a C toolchain for the N64’s CPU. On Windows use LLVM 18.1.8 (19.1.0 miscompiles MIPS), plus ld.lld and N64Recomp’s RecompModTool built from the same fork as the port.

Start from the mod template

The template has mod.toml, mod.ld, a Makefile, modding.h, and the decomp’s headers and symbol files as submodules. Add the port’s mod_api/jfg_api.h to your include path.

Fill in the manifest

In mod.toml:

[manifest] id = "yourname.less_blood" version = "1.0.0" display_name = "Less Blood" authors = ["you"] game_id = "jet-force-gemini" minimum_recomp_version = "1.1.8"

Write the code

src/main.c
#include "modding.h" #include "jfg_api.h" RECOMP_IMPORT("*", int recomp_printf(const char* fmt, ...)); RECOMP_CALLBACK("*", jfg_on_blood_spurt) void less_blood(JfgBloodSpurt* b) { b->amount = b->amount / 2; // 100 = the game's own amount } RECOMP_CALLBACK("*", jfg_on_screen_flash) void no_flash(JfgScreenFlash* f) { f->cancel = 1; } RECOMP_CALLBACK("*", jfg_on_level_change) void log_level(int level, int character) { recomp_printf("less_blood: level %d as character %d\n", level, character); }

Build

make RecompModTool mod.toml build

The .nrm lands in build/.

Install and check

Copy it into the port’s mods folder, turn it on, and play. bin/jfg_mods.log shows your lines; F9 › Mod API shows how often each event fires and how many mods listen.

Changing values the frame-rate-safe way

The port runs at 30 or 60 fps. Multiply anything you change per tick by jfg_get_tick_scale() (1.0 at 30 fps, 0.5 at 60), or use the frames argument of jfg_on_game_tick.

RECOMP_IMPORT("*", float jfg_get_tick_scale(void)); RECOMP_CALLBACK("*", jfg_on_game_tick) void every_tick(int frames) { timer += 1.0f * jfg_get_tick_scale(); }

Hooks and patches

For things the API doesn’t cover:

RECOMP_HOOK("SquaddieControl") void before_squaddie(void* obj) { /* runs before the game's function */ } RECOMP_PATCH void someGameFunction(void) { /* replaces it: only one mod may */ }

Game variables you read must be declared extern with the decomp’s exact type. See C mod API for the rules, and When mods meet for what happens when two mods change the same function.

Last updated on