Skip to main content

Token System

The token system allows you to embed dynamic values directly in dialogue text. Instead of hardcoding names, numbers, or world state, you write tokens that resolve at runtime.

Syntax

Tokens use curly braces with a prefix.key format:

Hello, {other.name}! You've visited me {bb.visit_count} times.

When the beat is displayed, the token service replaces each token with its resolved value:

Hello, Player! You've visited me 3 times.

If a token cannot be resolved, it remains in the text unchanged and a warning is logged.

To display a literal { character, use double braces: {{.

Built-in resolvers

Three token resolvers are registered by default.

Participant tokens

Access participant names during the conversation.

TokenResolves to
{speaker.name}The current speaker's display name
{other.name}The other participant's display name (the listener)

Blackboard tokens (prefix: bb)

Read values from the speaker's blackboard. All four value types are supported (string, int, float, bool).

TokenResolves to
{bb.key_name}Agent-scoped blackboard value
{bb.conversation.key_name}Conversation-scoped blackboard value

The resolver checks string first, then int, then float, then bool. The first match wins.

Example:

If the speaker's blackboard has quest_status = "in progress" and gold_offered = 50:

The quest is {bb.quest_status}. I'll pay you {bb.gold_offered} gold.

Resolves to:

The quest is in progress. I'll pay you 50 gold.

World tokens (prefix: world)

Access world clock data. Requires CIVIL-AI-SYSTEM to be installed (otherwise the world clock returns zero).

TokenResolves to
{world.time}Current time formatted as HH:MM
{world.day}Current day number
{world.period}Time period: morning, afternoon, evening, or night

Example:

Good {world.period}! It's day {world.day}.

Resolves to:

Good morning! It's day 3.

Where tokens work

Tokens are resolved in:

  • text beat content (the main dialogue text)
  • option labels (the text shown on dialogue choices)

Creating custom resolvers

You can add your own token resolvers by implementing ITokenResolver:

csharp
public class MyCustomResolver : ITokenResolver
{
public string Prefix => "custom";

public bool TryResolve(string key, TokenResolutionContext context, out string value)
{
value = null;

switch (key)
{
case "player_class":
value = GetPlayerClass();
return true;
default:
return false;
}
}
}

Register your resolver as a multi-registration for ITokenResolver in a module registrar with an order higher than 200.

Once registered, your tokens work everywhere: {custom.player_class}.

The resolution context

Every token resolver receives a TokenResolutionContext containing:

  • Participants -- the current speaker and listener
  • NodeId -- the ID of the current dialogue node (useful for debugging)

Practical advice

Tokens are most useful for:

  • personalising dialogue with character names
  • reflecting blackboard state without complex branching
  • showing world time in greetings or time-sensitive dialogue
  • creating reusable dialogue trees that adapt to different participants

Keep token keys short and consistent. A token that cannot resolve produces a warning log and displays the raw {prefix.key} text, so test your trees with the expected blackboard state.

Where to go next