Flags
State bits and variable conditions that gate actions, buttons, events and contexts.
Flags are named booleans that track the current state of a chat. Simple strings: is_thirsty, wears_hat, door_closed. You set them from actions, events, or scripts; you read them from conditions on actions and contexts.
A character with the flag wears_hat set will:
- Have the
wear_hataction filtered out (it's a no-op — they already wear it). - Have the
{{ char }} is wearing a hatcontext render in every prompt.
That's the core loop. Everything else is variations.
Setting and unsetting
A flag is set by including its name. Prefix with ! to unset:
wears_hat # set
!wears_hat # unsetYou can do both in the same effect — e.g. an action that takes off a hat and reveals a hairstyle might set !wears_hat, ponytail_visible.
Enum flags (mutually exclusive)
Some flags are mutually exclusive — a character can be standing OR sitting OR dancing, but not all three. Manually unsetting "all the other poses" every time you change is tedious. Voxta solves this with dot-syntax enum flags:
pose.standing
pose.sitting
pose.dancingSetting pose.dancing automatically unsets pose.standing and pose.sitting. The pose. prefix means "all flags starting with pose. are mutually exclusive."
Unsetting enums
!pose.standing— unsets onlypose.standing. Otherpose.*flags untouched.!pose— unsets allpose.*flags. Useful for resetting a category.
Flag conditions
Conditions are mini JS-like boolean expressions used on actions and contexts.
| Expression | Meaning |
|---|---|
pose.standing | Active only if pose.standing is set. |
!pose.standing | Active only if pose.standing is NOT set. |
pose.standing && !door.closed | Both must hold. |
hold.empty_bottle || hold.empty_glass | Either holds. |
(a || b) && !c | Use parens to group. |
Variable conditions
A condition can also read chat variables with $name, so you don't need a mirror flag for every number:
$gold >= 50 && !transit
$hp < 10 || wounded
$class == "mage"
$gold - $debt >= 0A missing variable makes comparisons false (and !$name true). Writing a referenced variable re-evaluates whatever reads it, so button visibility updates on its own.
Labels are templates too, so a button can show the value: Bribe the guard ({{ variables.bribe_cost }}g).
Setting from a script
import { chat } from "@voxta";
export function trigger(e) {
// Set a flag
chat.setFlag('wears_hat');
// Unset
chat.unsetFlag('wears_hat');
// Equivalent:
chat.setFlag('!wears_hat');
// Enum
chat.setFlag('pose.sitting'); // auto-clears pose.standing etc.
// Multiple at once
chat.setFlags('wears_hat', 'pose.standing', '!is_drenched');
// Check
if (chat.hasFlag('wears_hat')) { /* ... */ }
}See Scripting for the full chat-state API.
Expiring flags
Set a flag that auto-clears after a duration:
// Expires after 10 conversation messages
chat.setFlag('cooldown', { messages: 10 });
// Expires after 2 minutes of chat time
chat.setFlag('cooldown', { seconds: 120 });Useful for cooldowns, "just happened" markers, and short-lived emotional states.
Patterns
Once-ever events
Use the Once checkbox on an action — Voxta auto-generates an invisible _action_name flag and an !_action_name condition. Fires once per chat.
State machines
Use enum flags for state machines. state.intro, state.investigation, state.confrontation — only one is set at a time, and transitions are explicit.
Location / slot navigation
A state machine where the state is where the user is. One enum flag holds the location, and every context, action and button is gated on it:
Flag: places.lobby (or places.backyard, places.corridor, …)
Context: "{{ user }} is standing in the lobby…" filter: places.lobby
Button: "Go to the backyard" filter: places.lobby
→ sets places.backyardSetting places.backyard clears places.lobby, so the lobby context and buttons vanish and the backyard's appear — one flag, one source of truth. See the Stage Editor for the visual side.
Looping events
Anchor an event's trigger to a flag the event itself sets, with a message gap. It fires, re-sets the flag, re-arms, fires again.