Declaration shape
A mutable variable uses var. The optional mem modifier adds persistent storage where the variable would otherwise restart from its default:
var health = 100;
mem var charges = 5;
A calculated, read-only variable uses const:
const healthPercent = clamp((health / maxHealth) * 100, 0, 100);
App Main adds a scope before the declaration and requires a $ name:
player var $health = 100;
app const $eventComplete = $eventProgress >= $eventGoal;
host mem var $attempts = 0;
Every declaration ends with a semicolon.
Declaration keywords
var declares a mutable value. Sugar may assign a new value to it later.
mem modifies a local or hosted var declaration so JOI keeps its latest value. It is not a separate variable kind and cannot modify const.
const declares a value calculated from an expression. It cannot be assigned, reset, remembered, configured with with, or entered by the user. Random functions are not allowed in its expression.
App scopes
Every variable declared in App Main begins with one scope:
playergives each registered JOI a separate value.appgives the App one value shared by everyone.hostprovides the declaration separately to each linked Item or Boop.
The scope establishes ownership. The keywords after it still establish whether the declaration is mutable, persistent, or calculated.
Mutable Player and App values are persistent by definition, so those scopes do not use mem. A mutable Host value uses mem only when each host should keep its latest value.
An App supports up to 50 variables. App scopes cannot be used on declarations inside an Item, action, or Boop.
Types
Sugar normally infers the type from the initial value:
| Declaration | Runtime value |
|---|---|
var name = "Alex"; | str |
var score = 10; | number |
var enabled = true; | bool |
var mode: choice = "first"; | The string, number, or boolean type used by its choices |
var reward: Item; | An Item object selected from inventory |
var target: Joi = None; | A JOI selected in chat |
var targets: List<Joi> = (); | A list of JOIs selected in chat |
var target: Seat = None; | A Session seat selected in chat |
var targets: List<Seat> = (); | A list of Session seats selected in chat |
var art: Media = &cover; | A Media resource value |
var delay: Duration = Duration::minutes(15); | A Duration value |
var history = ("one", "two"); | A list value |
| `let transform: Fn = | value |
Scalar annotations are optional: str, number, and bool. The choice type and compound Item type select JOI-specific input controls. Joi, List<Joi>, Seat, and List<Seat> use JOI’s existing chat pickers. Date, Duration, Media, and Pronoun preserve their matching value type and cannot use input: true. Fn and lists containing Fn may only be held by execution-local let variables.
Scalar types are lowercase. Compound types are PascalCase.
In List<Joi>, List is the collection type and Joi is its type argument, meaning every entry is a Joi value. Read it as “list of JOIs.” Every Sugar type may be a type argument, including scalars and lists: List<number>, List<str>, List<bool>, and List<List<number>> are all valid.
Labels and with
Use the display arrow -> when the visible label should differ from the variable name:
var hp -> "Health" = 100;
Use with for input and display configuration:
mem var mood -> "Current mood": choice = "happy" with {
input: true,
required: true,
choices: ("happy", "sad", "very_excited" -> "Very excited"),
};
| Setting | Meaning |
|---|---|
input | Show a control before Sugar runs |
required | Require an answer. This needs input: true |
min | Lowest value accepted by a number input |
max | Highest value accepted by a number input, or None for no maximum |
choices | Values offered by a choice input |
count | Exact or ranged number of entries for List<Joi> and List<Seat> inputs |
A plain string choice such as "very_excited" receives the label Very excited. Write "very_excited" -> "Thrilled" to display a different label while keeping "very_excited" as its Sugar value. Values in a choice may be strings, numbers, or booleans, but one variable cannot mix those types. Its initial value must use the same type and appear in choices. The label after -> remains a string.
Joi and Seat inputs can limit their picker with a list expression:
var rival: Joi = None with input => true, required => true, choices => JOIS;
var rivals: List<Seat> = () with {
input: true,
required: true,
choices: SEATS.filter(|seat| seat != SELF),
count: 1..=3,
};
JOI accepts only values from that list when the action runs. count accepts 1 through 6 entries.
min and max constrain the numeric value held by one number variable. count constrains how many entries are held by one List<Joi> or List<Seat> variable. They do not configure the same thing.
Location and availability
Where a local declaration appears determines where its name is available:
| Location | Availability |
|---|---|
| Item top level | Every custom action on that Item |
| Item action | Only that action |
| Boop top level | That Boop |
Current program with let | That program; an action’s let values also reach its texts |
An Item, Item action, or Boop supports up to 50 variables in each declaration scope.
An Item-level Memory variable belongs to one non-stackable Item. An action-level Memory variable belongs to that action. A Boop Memory variable is stored separately for each JOI using the Boop.
Stackable Items cannot remember variables because JOI cannot attach a separate value to each copy.
Storage
Storage follows from the declaration modifiers and App scope:
- A local or hosted mutable variable restarts from its default unless it has
mem. - A mutable Player value is stored separately for each registered JOI.
- A mutable App value has one stored copy for the App.
- A constant has no stored result and is recalculated from its expression.
For a hosted Memory variable, an Item stores the value on its individual instance. A Boop stores a separate value for each JOI using it. A linked creation can change its starting value with defaults.
If a person cancels a required confirmation, JOI saves none of the changes made by that run.
Assignments
Item action and Boop programs can assign:
- Mutable local declarations
- Mutable App declarations available in that context
letvariables created by the current program
They cannot assign:
- Constants
- Item objects selected from inventory
- App variables outside the current Automation scope
- Variables supplied to an Embed Template by its caller
Conditions cannot assign outside variables. An Embed Template may change only the let variables it creates.
Names and value limits
Variable names contain up to 32 characters. They begin with a letter or underscore, followed by letters, numbers, or underscores. App names use the same rules after $.
str variables contain up to 2,000 characters. Lists and objects must stay within the collection limits.