Calling C++ from a script
A script can define fn add and call it. That never leaves ReScripted.
To touch your game — create an entity, write to the console, read a score — the script calls a name you implemented in C++.
let id = spawn("goblin", 12);
engine.bind("spawn", /* your lambda */);
That is a host function. It lives on the engine. Unloading a script does not remove it.
import math is a different feature. That is a library. Use bind when the name should always be there.
print, len, str, type
The engine ships these four names.
len, str, and type do what you expect.
print concatenates its arguments and returns that string. It does not write to stdout.
To print to the console:
engine.bind("print", [](const std::vector<rescripted::Value>& args) {
for (const auto& arg : args) {
std::cout << arg.to_string() << '\n';
}
return rescripted::Value();
});
List the names you bind in builtins.functions in the language JSON.
bind — a function
engine.bind("spawn", [](const std::vector<rescripted::Value>& args) {
if (args.size() < 2) {
throw rescripted::RuntimeError("spawn(name, hp)");
}
const std::string name = args[0].as_string();
const double hp = args[1].as_number();
// create the entity in your game
return rescripted::Value(1.0); // id
});
Arguments arrive as rescripted::Value. Return a Value (or an empty Value() for “no result”). Throw RuntimeError for bad arguments.
unbind("spawn") removes it.
Anything you bind is visible to every script. Do not bind the filesystem or the network unless every script should have that power.
bind_type — a constructor
engine.bind_type("vec2", [](const std::vector<rescripted::Value>& args) {
// build and return a map or your own representation
return rescripted::Value(std::map<std::string, rescripted::Value>{
{"x", args.size() > 0 ? args[0] : rescripted::Value(0.0)},
{"y", args.size() > 1 ? args[1] : rescripted::Value(0.0)},
});
});
let p = vec2(1, 2);
print(p.x);
This is Name(...). It is not the same as a type block in a script:
entity Hero {
hp = 10;
}
That form comes from custom.types in the JSON. run() stores a map under Hero. You do not bind_type for it.
bind_decorator — a hook at load
std::vector<std::string> exported;
engine.bind_decorator("export", [&](const std::string& name, const rescripted::Value&) {
exported.push_back(name);
});
In the language JSON, declare the decorator (custom.decorators, prefix @). In a script:
@export
fn ready() {
return 1;
}
When that file is loaded, the hook runs once with "ready". It does not run again when something calls ready.
The prefix must not be a comment marker. If # starts a line comment, @ is a safer prefix than #.
Unknown decorator names are ignored. If a missing export should be an error, check exported yourself after load.