Custom function kinds, types, and decorators
Use custom only when the universal schema has no field for the kind of declaration. Do not put if syntax in custom.
custom is a free object in the template. The engine reads these arrays if present:
"custom": {
"function_kinds": [
{
"name": "command",
"declaration_keywords": ["command"],
"description": "Host-invoked command. Same call semantics as fn."
},
{
"name": "quest",
"declaration_keywords": ["quest"],
"description": "A scripted quest hook."
}
],
"types": [
{
"name": "entity",
"declaration_keywords": ["entity"],
"description": "Named map of fields the host may interpret."
},
{
"name": "item",
"declaration_keywords": ["item"],
"description": "Item definition block."
}
],
"decorators": [
{
"name": "export",
"prefix": "@",
"description": "Register with the host on load."
},
{
"name": "tick",
"prefix": "@",
"description": "Run every frame from C++."
}
],
"host_functions": ["print", "spawn", "damage"]
}
Also add those keywords to keywords.reserved / declaration / control_flow as appropriate so they tokenize as keywords. The reader appends declaration_keywords from custom kinds onto the function-keyword list automatically.
Function kinds
command ping() { return "pong"; } parses like fn ping() { ... }. The keyword is whatever you listed. Host:
engine.call("ping");
Several kinds can coexist (fn, command, quest). They share the same runtime function object.
Types
entity Portal {
x = 10;
y = 4;
dest = "keep";
}
After run, engine.get("Portal") is a map with string keys. The host decides that this map is a “portal.”
Fields are name = expression. Separators: comma or statement terminator.
Decorators
@tick
@export
command think() {
return 0;
}
Multiple prefixes in a row are allowed. Each name is looked up in bind_decorator. Unknown decorator names are ignored (no error). If the user wanted a hard failure, check the name in a catch-all hook or validate after load by walking get.
What custom is not for
- Renaming
if— usecontrol_flow.conditional.if_keywords - New operators — use
operators/custom_operatorsin the template; engine usesoperators.*and precedence - Entirely different JSON shapes — forbidden
- Language-specific C++ in ReScripted — forbidden
Document every custom kind’s meaning in description so the next agent does not guess.