SlormAPI lets other mods (and, optionally, development tools) read and change The Slormancer while it runs: game
variables, instances, scripts and built-in functions, script hooks, game events, and drawing with the game's own
sprites and fonts — including buttons in the game's own menus that hover, click and slide like native ones. Mods use
it from C++ or as Lua mods: one text file in mods\lua, loaded while the game runs.
On its own it adds two things to the game's menus:
- a MODS button next to DISCORD in the main menu and the pause menu. It lists the installed mods with their
versions (5 per page) and switches mods on or off: a switched-off mod is renamed to
X.dll.disabled, so it is not loaded from the next game start (Lua mods switch at once). SlormAPI and the loader cannot be switched off there, and mods installed with Vortex are switched in Vortex. - a Mods tab in Options with the settings of every mod that has some, drawn like the game's own settings.
The Mods page also warns when something is wrong: a mod whose requirements are not met ("Needs SlormAPI 1.0.0"), a Lua mod with an error, a mod whose game functions were not found after a game update, and a game that crashed last time (the button then reads MODS (!)).
Example mods: examples/lua/kill_counter.lua here, and BuildFilter, a
loot filter made twice - in C++ and in Lua - with the same features.
- The Slormancer (Windows, Steam version)
- The Aurie mod loader (Aurie Core 2.0.2 + YYToolkit 5.0.0): the installer below sets it up. A game that already loads Aurie mods another way works as it is.
Easiest: SlormAPI-Installer-1.0.3.zip from the Releases. Unzip
it, close the game, double-click Install.bat and choose 1. It installs the Aurie loader (the official Aurie Core
2.0.2 + YYToolkit 5.0.0c, checked against their release checksums) and SlormAPI, and can repair the loader after a
game update or remove everything. When the game already loads Aurie through another mod loader (a version.dll
loader), it keeps that loader and adds only the mod.
Manually (SlormAPI-1.0.3.zip, the game-folder layout: mods/Aurie/SlormAPI.dll):
- Have Aurie + YYToolkit for The Slormancer (https://github.com/AurieFramework/Aurie, https://github.com/AurieFramework/YYToolkit), or a game where Aurie mods already load.
- Unzip it into
<The Slormancer folder>.
With Vortex: install The Slormancer's Vortex extension and its loader first, then add SlormAPI-1.0.3.zip in
Vortex like any other mod (it goes to mods/Aurie). Don't use the installer then. If you used it before, run it once
more and choose 4: the Vortex loader setup needs the original game exe back. Mods that Vortex deployed show "Switched
on / off in Vortex" on the Mods page.
Uninstall: delete SlormAPI.dll (or installer > 4). SlormAPI never writes your save files.
Full reference: docs/API.md — every command, event, draw program, widget and Lua function in detail, the item format, a game cheat sheet, and how to start a C++ or Lua mod. The short version:
Copy source/SlormAPI_Interface.hpp into your Aurie module and get the interface (modules can load in any order, so ask
for it from a game callback, not in ModuleInitialize):
#include "SlormAPI_Interface.hpp"
Aurie::AurieInterfaceBase* base = nullptr;
if (AurieSuccess(Aurie::ObGetInterface(SLORMAPI_INTERFACE_NAME, base)))
{
auto* api = static_cast<SlormAPIInterface*>(base);
const char* r = api->Request(R"({"cmd":"get","path":"current_hero"})"); // {"ok":true,"result":0}
}Call Request on the game thread (from a YYToolkit callback). Every request is one JSON object with a cmd:
| cmd | fields | does |
|---|---|---|
ping |
version, room, fps, game_version, last_session (did the last one crash) |
|
get / keys |
path ("a.b[3]"), root ("global", an object name, or {"$inst": id}), depth |
read a value / list members |
set |
path, value, root |
write a value (arrays are written in place) |
call |
fn, args, self |
call a game script or built-in function |
instances |
object, vars |
instances of an object (+ chosen variables) |
objects / scripts |
asset lists | |
hook |
owner, script or scripts, capture, capture_args, depth, overrides, after, ttl_ms, post_set |
hook game scripts (below) |
unhook |
owner, script (none = all your hooks), all |
remove your hooks |
hooks / hooklog |
– / owner, max, consume |
list hooks (with each owner's layer) / read your captured calls |
register_mod |
name, version, author, description, file, requires, game |
show your mod on the Mods page; requires e.g. {"SlormAPI": "1.0.0"}, game the game version it was made for |
mods / mods_page |
– / enabled, open |
the installed mods / switch the Mods page off or open it |
mod_enable |
file ("X.dll" or "Folder/X.dll"), enabled |
switch a mod of mods/aurie on or off for the next start |
overlay |
event, vars, when, calls, ttl_ms |
a draw program run after an object event (default the GUI draw) |
subscribe / unsubscribe / events |
owner, events / owner, events / owner, max |
game events (below) |
event_types |
the event names | |
ui_add / ui_remove / ui_refresh |
owner, menu, program / owner, menu / – |
your drawing and widgets in a game menu (below) |
item_info |
item (an item array) or inst (a dropped item) or equipped (0-10: helm .. cape) |
the item in readable form: slot, level, rarity, legendary, every stat with its name and value, extras |
stat_info / legendary_info / skill_info |
stat (+ slot, roll, level) / id / hero, id |
names and data from the game's own files; stat_info with a roll also gives its value on an item of level (100) |
slot_info |
slot (helm .. cape), hero |
the gold (epic) stats that can roll on that slot and the hero's lootable legendaries for it, by name |
stat_list / hero_stats |
– | every stat of the game with its unit / the hero's current final stats, life, mana and Light Shield |
register_legendary / legendaries / legendary_equipped / legendary_drop |
see Custom legendaries | new legendary items |
buff_add / buff_remove / buff_stacks |
target, key, stats, duration, max_stacks, icon, name |
the game's buffs: stats, stacks, timers, the buff bar |
mod_data |
mod, value |
saves one JSON value for your mod (up to 4 MB) / without value: returns it (null when none) |
mod_log |
mod, line |
appends a timestamped line to your mod's log file |
register_settings / settings / set_setting |
mod, settings / mod / mod, key, value |
mod settings (below) |
batch |
cmds |
several commands in one frame |
In JSON values, {"$inst": id} is an instance and {"$asset": "spr_name"} an asset index.
Owners: pass "owner": "<YourMod>" to hook, unhook and hooklog. Each owner gets its own layer on a script
(overrides, program, capture) and its own log, so mods hooking the same script do not replace each other's hooks or
read each other's calls. Without owner you share the "" layer with development tools. capture_args logs only
calls whose arguments match (same patterns as overrides), e.g. only your own button clicks.
Hook overrides: {"args": [pattern...], "value": v} replaces the return value; add "skip": true to not run the
script at all, or "set_args": {"2": "new"} to run it with arguments replaced. Pattern entries: null = any,
"abc*" = prefix.
Draw programs (overlay, and a hook's after, which runs inside the hooked script — e.g. inside a menu bake):
{"vars": [[name, expr]], "when": expr, "calls": [{"fn": "draw_sprite", "args": [...], "when": expr}]}.
Expressions: literals, {"$v": name}, {"$self": var}, {"$global": var}, {"$arg": i} (the hooked call's
argument), {"$fn": name, "args": [...]}, {"$op": op, "args": [...]} with + - * / == != < <= > >= and or not ? ?? def in_rect mba. Pseudo calls: $mba_set [name, index, value], $mba_push [entry] (replaces an entry with the same
name and position) and $mba_remove [index, value] edit obj_ui_next_gen.mba, the game's list of clickable UI
elements [name, sprite, x, y, w, h, text, script, argument, tooltip]; $set [var, value]. ttl_ms makes an
overlay / hook stop applying unless it is renewed. Text: the game's scr_draw_text takes fonts by name, e.g.
[x, y, "text", 1, 1, "ft_standard_16", halign, valign, 0, 1, "white", "black", 1, false] (with 1, 0 instead of
0, 1 the game defers the text and it may never be drawn).
Instead of checking the game every frame, a mod can be told when something happens. Events are delivered once per frame, right after they happened, on the game thread.
| event | data |
|---|---|
enemy_killed |
enemy ($inst, $object), x, y, level, profile (trash / good / elite / boss) |
loot_dropped |
loot (the obj_loot instance), item (the item array, as in the save files), info (as item_info) |
item_picked_up |
items, picked |
gold_changed |
gold, delta |
hero_hit |
damage, attacker, life, max_life, shield, max_shield |
hit_dealt |
target, skill, strike (normal / critical / ancestral) — every hero hit |
damage_dealt |
target, damage (the health it lost) |
status_applied / status_removed |
status (fire_burn, ice_chill, knight_bleed...), name, target, + duration, stacks / reason |
cooldown_started |
skill, name, hero, cooldown |
stats_refreshed |
hero (the hero's stats were recalculated) |
hero_died |
– (once per death) |
level_up |
level, previous, hero (0 Knight, 1 Huntress, 2 Mage) |
room_changed |
room, previous |
menu_changed |
menu, previous (obj_ui_next_gen.menu_show: none, main, inventory, ...) |
skill_cast |
skill, name, hero |
boss_killed |
as enemy_killed |
item_equipped |
slot, index, hero, item, previous, info (also when a slot is emptied) |
run_started / run_ended |
– |
setting_changed |
mod, key, value |
ui_clicked |
owner, id, action (a click on one of your widgets) |
C++:
api->Subscribe("enemy_killed", [](const char* Event, const char* DataJson, void* User) { /* ... */ }, nullptr);Subscribe returns an id for Unsubscribe; "*" means every event. A callback that crashes is unsubscribed and logged
instead of taking the game down. JSON / TCP: {"cmd":"subscribe","owner":"MyMod","events":["loot_dropped"]}, then
{"cmd":"events","owner":"MyMod"} returns (and removes) what happened since the last call. Events that need a game
script hook install it only while someone listens.
A Lua mod is one text file: put my_mod.lua into <The Slormancer>\mods\lua\ (SlormAPI creates the folder). It loads
while the game runs, reloads every time you save the file, and shows on the Mods page, where it switches on and off
right away. examples/lua/kill_counter.lua is a complete one (kills in the pause menu, a Reset button, a setting).
To publish one for Vortex, zip it as a pack: mods/lua/my_mod/lua-pack.json next to mods/lua/my_mod/my_mod.lua.
Vortex installs it automatically (into mods/Lua/my_mod/) and SlormAPI runs it from there:
{ "schema_version": 1, "type": "slorm-lua", "id": "my_mod", "name": "My Mod", "version": "1.0.0",
"entry": "my_mod.lua", "requires": { "slorm.slormapi": "1.0.2" } }"slorm.slormapi" is what marks it as a SlormAPI mod (the minimum SlormAPI version); packs without it are left alone.
Its name, author and description show on the Mods page (no slorm.mod{} needed in a pack, 1.0.3+); its data
files are named after the id.
Starting a new one: copy examples/lua_pack/ - a pack ready to rename, with pack.cmd, which checks
lua-pack.json and makes the zip for Nexus / Vortex.
slorm.mod{ name = "Kill Counter", version = "1.0", author = "me", description = "Counts your kills" }
local kills = 0
slorm.on("enemy_killed", function(e)
kills = kills + 1
slorm.ui("main", { slorm.text(1300, 120, "Kills: " .. kills, "ft_standard_16") })
end)| function | does |
|---|---|
slorm.mod{name, version, author, description} |
your mod's line on the Mods page |
slorm.on(event, fn) / slorm.off(id) |
game events (the table under Events); fn(data, event) |
slorm.every(seconds, fn) / slorm.after(seconds, fn) / slorm.cancel(id) |
timers |
slorm.get(path, root?) / slorm.set(path, value, root?) / slorm.call(fn, ...) / slorm.instances(object, vars?) |
the game's variables, scripts and instances |
slorm.settings{...} / slorm.setting(key) |
your settings in Options > Mods (as register_settings) |
slorm.ui(menu, calls) / slorm.ui_remove(menu) |
your widgets in a menu; slorm.text, slorm.button, slorm.radio, slorm.selector make them |
slorm.on_click(function(id, action) ... end) |
clicks on your widgets |
slorm.item_info(item) / slorm.slot_info(slot, hero?) / slorm.stat_info(stat, slot?, roll?, level?) |
game knowledge (the commands above) |
slorm.hero_stats() / slorm.stat_list() |
the hero's final stats / every stat with its unit |
slorm.legendary{...} / slorm.buff{...} |
a custom legendary (with on_hit, on_kill, ... handlers) / a game buff |
slorm.save_data(table) / slorm.load_data() |
keep your mod's own data between games (mods/aurie/SlormAPI/data) |
slorm.log_file(...) |
a line in your mod's own log file (next to its data) |
slorm.clock() |
seconds since the game started (for time budgets) |
slorm.null |
JSON null where Lua has no nil, e.g. "any" in hook patterns: { slorm.null, "abc*" } |
slorm.request{cmd = ..., ...} |
any other command of this README (your mod is the owner), e.g. hook with draw programs |
slorm.log(...) / print(...) |
a line in Aurie's log |
A call that runs longer than 200 ms is stopped (no frozen game), and an error is shown on the Mods page ("! Lua error:
...") and in the log; fix the file and save to reload it. SlormAPI embeds Lua 5.5.1 (MIT license; its source is in third_party/lua).
What a Lua mod cannot do (a Lua mod is a text file anyone can read before installing it, but it should still not be able to harm your PC):
- Lua's own file, OS, package and debug libraries are not loaded;
loadtakes text only (no precompiled code) andstring.dumpis gone. - Game engine functions that reach outside the game are refused, in
slorm.calland in widget programs: files (file_*,directory_*,ini_*,buffer_save/load,screen_save, ...), the network (http_*,network_*,url_*,steam_*), the OS (execute_*,environment_*,os_*,external_*), dialogs and the clipboard. - A Lua mod acts as itself only: its own hooks, events, widgets, settings and data file. It cannot switch mods on / off, change another mod's settings or data, or remove another mod's hooks.
- Each Lua mod may use at most 64 MB of memory, and each call at most 200 ms.
It can change the game itself (that is what mods are for), including through the game's own scripts.
Put your own controls into any game menu by its name (main, inventory, settings, skills, elements,
profile, ...); they are drawn with the game's own sprites and fonts, hover like the game's buttons, show the game's
tooltip, and their clicks come back as the ui_clicked event:
{"cmd": "ui_add", "owner": "MyMod", "menu": "inventory", "program": {"calls": [
{"fn": "$text", "args": [1300, 120, "My mod", "ft_standard_16", "white"]},
{"fn": "$button", "args": ["go", 1300, 160, "Do it", {"$asset": "spr_menu_thin_grey_button"}, "A tooltip"]},
{"fn": "$radio", "args": ["auto", 1300, 240, "Automatic", true]},
{"fn": "$selector", "args": ["mode", 1300, 290, "Mode", "Normal"]}]}}| widget | args | click actions |
|---|---|---|
$button |
id, x, y, text, sprite?, tooltip? (default sprite: the thin grey menu button) |
click |
$radio |
id, x, y, label, on, tooltip? (the game's diamond switch) |
toggle |
$selector |
id, x, y, label, value, tooltip? (the game's ◀ value ▶ box) |
prev, next |
$text |
x, y, text, font?, color?, halign? (fonts by name, e.g. ft_standard_12) |
– |
Positions are the game's 1920×1080 layout; widgets follow the game's ui_scale (from the resolution) like its own
controls.
Your program runs every time the game draws that menu, so values in it can come from game variables ({"$global": ...}) or from your own state: change it with ui_add again (or ui_refresh if it reads game variables). Widgets
you stop drawing are removed, and ui_remove takes everything of yours out of that menu.
Mods can add legendary items. They are rows of the game's own legendary table, so they drop like the game's legendaries, show their name, icon and description, and count as worn like the game's own; their stats (scaled by the item's roll) and effects - buffs and heals on hits, crits, kills, casts, statuses or a timer - come from SlormAPI:
slorm.legendary{
id = "windrunner", name = "Windrunner", slot = "boot", icon = 84,
description = "+@% Movement Speed. Critical strikes grant +@% Attack Speed for 3 seconds.",
stats = { the_speed_global_mult = 40 }, values = { 25 },
effects = { { trigger = "crit", cooldown = 0.5,
buff = { stats = { attack_speed_percent = 25 }, duration = 3, name = "Windrunner Haste", icon = "slow" } } },
}They never reach a save file: the game saves each one as an Epic with the same stats, which becomes the legendary again
after loading. Removing the mod leaves an ordinary Epic, and the player is asked before playing such a save (Load
anyway / Back). Details: docs/API.md;
a complete example: examples/lua/legendary_windrunner.lua.
Describe your settings once (e.g. when your mod starts); SlormAPI shows them in Options > Mods, saves the values in
mods/aurie/SlormAPI/settings/<mod>.json and returns the current values:
{"cmd": "register_settings", "mod": "MyMod", "settings": [
{"key": "enabled", "label": "Show the overlay", "type": "bool", "default": true},
{"key": "hotkey", "label": "Hotkey", "help": "While the game has the focus", "type": "choice",
"options": ["Off", "F5", "F6", "F7"], "default": "Off"},
{"key": "size", "label": "Size", "type": "number", "min": 50, "max": 200, "step": 10, "default": 100}]}Types: bool (a switch), choice (one of options) and number (min..max by step), both shown as the game's
selector. Read them with settings, change them with set_setting, and subscribe to setting_changed to react when
the player changes one.
For external tools, SlormAPI can serve the same commands as newline-delimited JSON on 127.0.0.1:47950 (loopback
only). It starts only when the environment variable SLORMAPI_SERVER=1 (or SLORMAPI_PORT) is set, or an empty file
SlormAPI.server sits next to the DLL. While it is on, any program on your PC can control the game - leave it off
for normal play. Connections that start like an HTTP request are dropped (a web page cannot reach it through your
browser), and a line longer than 16 MB ends the connection.
Visual Studio 2022 (or Build Tools) with the C++ desktop workload and a Windows 10 SDK:
msbuild SlormAPI.vcxproj /p:Configuration=Release /p:Platform=x64
Output: bin/SlormAPI.dll. The Aurie / YYToolkit SDK headers it builds against are in third_party/YYToolkit
(AGPL-3.0, from the YYToolkit 5.0 example plugin) and Lua 5.5.1 in third_party/lua (MIT). Mods using SlormAPI need
only source/SlormAPI_Interface.hpp (and source/Json.hpp if they want the same small JSON helper).
The player installer (installer/: Install.bat, installer.ps1) is built into a zip with
python installer/build.py --loader <folder with the official AurieCore.dll, AuriePatcher.exe, YYToolkit.dll>
(the files of the Aurie v2.0.2 and YYToolkit v5.0.0c releases; their checksums are verified). --mod <X.dll> adds a
mod to it: BuildFilter's installer is this one with --mod BuildFilter.dll.
AGPL-3.0. Source code: this repository.


