Skip to content
Version v0.35 ยท supported
[WIP] Pending copyedit and approval.

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.