Variables and scopes¶
A variable is a named value a profile remembers. A command can read it to make
a decision, write it to record state, and, with the right declaration, keep it
across a restart. Every value is a string. Convert with tonumber and
tostring when you need a number.
Reading and writing¶
Two functions cover the whole surface:
local mode = amri.vars.get("mode") -- read
amri.vars.set("kills", tostring(n)) -- write in place
amri.vars.set("profile", "mode", "combat") -- write to a named scope
amri.vars.get(name) takes a single name and reads up the scope chain,
returning the first value it finds, or nil if the name is unset everywhere.
Supply a fallback with amri.vars.get(name) or "default". Passing a scope to
get is an error: the scope is always implicit.
amri.vars.set has two forms:
| Form | Effect |
|---|---|
amri.vars.set(name, val) |
Updates the variable wherever it already lives. If the name is unset everywhere, it is created at command scope, or at profile scope when there is no command running. |
amri.vars.set(scope, name, val) |
Writes to the named scope. If the name already exists at a different scope, the call errors instead of creating a shadowing copy. |
The scope chain¶
Variables live at four scopes. A read walks them from the inside out and stops at the first match:
command โ category โ profile โ global
| Scope | Lives for |
|---|---|
command |
Across invocations of the running command |
category |
While the command's category is active |
profile |
While the profile is loaded |
global |
Across profiles, defined in engine settings |
Because get walks outward, an inner scope hides a same-named value in an outer
one. A plain amri.vars.set(name, val) updates the binding it finds on that same
walk, so it changes the value at the scope where the name already lives rather
than making a new one closer in.
Shadow check on explicit writes
amri.vars.set(scope, name, val) refuses to create a variable that already
exists at another scope. If you meant to update the existing one, drop the
scope argument and use the two-argument form. If you meant a separate value,
pick a different name.
Declaring variables in TOML¶
Declare a variable to give it a starting value. Profile-scope variables go under
[profile.variables]; category and command scopes have matching tables.
# Ephemeral: reset to these values every time the profile loads
[profile.variables]
combat_mode = "false"
score = "0"
# Category-scope, ephemeral by default
[category.combat.variables]
ammo_count = "100"
A variable that is not declared starts unset, so a script should still guard with
amri.vars.get(name) or "default".
Persistence¶
An ephemeral variable resets to its declared value on every load. To make a value
survive a profile reload or a daemon restart, declare it under a
persistent_variables table instead:
# Saved to state.toml, restored on the next load
[profile.persistent_variables]
high_score = "0"
ship_loadout = "default"
Persistent values are stored in state.toml, a file the engine writes beside
profile.toml in the profile directory. On unload or shutdown, every persistent
variable is written there. On load, state.toml is read back and its saved values
override the declared defaults. Ephemeral variables always use their declared
defaults and are never written.
state.toml is generated and maintained by the engine. Do not edit it by hand,
and do not ship it with a shared profile: it holds one user's state.
At runtime the distinction is invisible. A script reads a persistent variable with
the same amri.vars.get(name) as any other.
See Functions for how these calls fit into a command's response,
Command context for what the running command exposes to a script,
and the API reference for the exact amri.vars signatures.