Widgets
Widget Drawing Order
Every widget supports a layer index attribute in its ui section, higher layer values are drawn later. Except the game widget which is always drawn first. The default layer value is 0.
[ui]
layer = 1
Game Widgets
Use a widget with role = "game" to render the game inside a screen. The game layer is always drawn first and the other widgets on top of it.
UI Section
- role = "game" draws the game output into this widget region.
- grid_size (2D): pixel size of one tile (zoom level); if omitted, it falls back to [viewport].grid_size.
- upscale (3D): render at (widget size ÷ upscale) and scale back up; 1 keeps native size, higher values soften, lower values sharpen.
- chunk_load_radius: number of chunk rings around the player that should be loaded immediately. Default: 2.
- chunk_prefetch_radius: wider background streaming radius around the player. Chunks outside this radius are unloaded. Default: 5.
- chunk_build_budget_near: max chunk builds per frame while near chunks are still missing. Higher values reduce startup wait but can cost frame time. Default: 10.
- chunk_build_budget_far: max chunk builds per frame after near chunks are loaded (background streaming). Default: 2.
On startup, chunks around the player are built first. Farther chunks inside chunk_prefetch_radius are streamed in later.
Camera Section
- type: Rendering camera mode; iso, firstp, or 2D.
- azimuth (iso, optional): horizontal rotation in degrees. If omitted, camera default is used.
- elevation (iso, optional): vertical angle in degrees. Higher values look more top-down and help seeing inside buildings. If omitted, camera default is used.
- scale (iso, optional): orthographic half-height (zoom). Larger values zoom out. If omitted, camera default is used.
Legacy aliases are still accepted for compatibility: azimuth_deg, elevation_deg.
This setting only affects rendering. It does not change how player input controls the camera.
To define player camera behavior, see set_player_camera.
Camera Safe Area Section
Use [camera_safe_area] on a 2D game widget when HUD widgets overlay part of the game view. The game still renders into the full widget, but the player is framed inside the remaining usable area.
[camera_safe_area]
right = 360
bottom = 96
- left, right, top, bottom: safe-area margins in widget pixels. Omitted values default to
0. - The safe area affects 2D camera framing only. It does not clip rendering or change input hit testing.
Say Section
Use [say] on a game widget to style speech bubbles produced by say(...).
These settings are presentation-specific, so different game widgets can use different bubble colors or backgrounds.
[say]
duration = 1.0 # How long say bubbles stay visible, in in-game minutes.
default = "#E5E501" # Default text color when category is empty or unknown.
background_enabled = true # Draw a background rectangle behind say text.
background_color = "#00000080" # Background RGBA color (#RRGGBBAA).
# Optional per-category text colors:
npc = "#FFFFFF"
warning = "#FF6666"
quest = "#66CCFF"
- duration: Lifetime of a
say(...)bubble in in-game minutes. The runtime uses the first active game widget duration and falls back to1.0. - default: Fallback text color for
say("Text")or unknown categories. - background_enabled: Enables or disables the bubble background rectangle.
- background_color: Background color including alpha, for example
#00000080for 50% black. - Category keys: Any extra key in
[say]is treated as a text color category used bysay("Text", "category_name").
Legacy projects with global game-config [say] still work as a fallback, but new projects should keep this section on the game widget.
Examples
[ui]
role = "game"
grid_size = 40
upscale = 1.5
chunk_load_radius = 2
chunk_prefetch_radius = 5
chunk_build_budget_near = 10
chunk_build_budget_far = 2
[camera]
type = "firstp"
[say]
default = "#E5E501"
warning = "#FF6666"
background_enabled = true
background_color = "#00000080"
[ui]
role = "game"
grid_size = 40
[camera]
type = "iso"
azimuth = 135.0
elevation = 50.0
scale = 6.0
Button Widgets
Button widgets define interactive UI elements that trigger commands when clicked.
Buttons are visually styled using the tiles assigned through the Tile Picker dock, or with text styling for menu/start-screen buttons.
In the HUD you can select between two icons per sector:
- The default (normal) state.
- The selected state is shown when the button is active or when its bound
valuematches the currentbindvalue.
If only those two icons exist, Eldiron keeps using them. Pressed buttons temporarily use the selected icon as their fallback; disabled buttons use the normal icon plus the disabled/cooldown overlay.
UI Section
- command - Preferred input binding. Use
control.forward,intent.attack,rules.basic_attack, orui.inventory. - action - Legacy movement action such as
forward; loaded ascontrol.forward. - intent - Legacy intent such as
use; loaded asintent.use. - spell - Legacy spell template name used when
intent = "spell"; loaded asintent.spell:<template>. - inventory_index - Draw and interact with the inventory item at that slot using the intent.
- equipped_slot - Draw and interact with the equipped item in that slot (for example
main_hand,off_hand) using the intent. - party - Optional UI binding target for this widget. If omitted, Eldiron uses the current leader for backward compatibility. Supported values today are
leader,party.0,party.1, ... and named bindings that match a character'sparty_roleorname. - portrait - If
true, the button draws the bound character'sportrait_tile_idinstead of an inventory/equipped item. - drag_drop - Enable drag-and-drop interaction for this inventory/equipped slot button.
- show / hide - Toggle specific widgets when clicked.
- deactivate - Turn off other buttons when clicked.
- active - Set this button’s state to active by default.
- bind / binding - Optional UI state key updated by this button, for example
start.class. - value - Value written to the bound UI state key when clicked.
- selection - Use
"single"for mutually exclusive choice buttons. A button is selected when itsvaluematches the current value ofbind. - group - Optional group name. Buttons with
bindandgroupdefault to single-choice behavior. - camera - Switch game widget rendering camera:
2d,iso,firstp. - player_camera - Switch player input mapping camera mode on the server:
2d,iso,firstp. - camera_target - Optional target game widget name; if omitted, applies to all game widgets.
- border_size - An optional border for the button. Default size is 0 (no border).
- border_color - The color for the border. Default is white ("#FFFFFF").
- text / label - Optional centered button text.
- font - Font used by text buttons.
- font_size - Text size for text buttons.
- color - Text color for text buttons.
- background_color - Optional button background color. Supports alpha, for example
"#111111cc".
Button state colors can be set either as flat fields or with nested style tables. Nested tables keep larger widgets more readable:
[ui]
role = "button"
text = "Warrior"
background_color = "#111111cc"
border_color = "#888888"
[ui.style.hover]
background = "#222222dd"
border = "#aaaaaa"
[ui.style.selected]
background = "#3a3320dd"
border = "#eed676"
text = "#fff1a8"
[ui.style.pressed]
background = "#262018dd"
border = "#d8bd5f"
[ui.style.disabled]
background = "#080808aa"
border = "#555555"
text = "#888888"
Equivalent flat fields are also supported: hover_background_color, hover_border_color, hover_color, selected_background_color, selected_border_color, selected_color, pressed_background_color, pressed_border_color, pressed_color, disabled_background_color, disabled_border_color, and disabled_color.
Buttons with rules.* commands resolve their label, description, costs, requirements, and cooldown display from the active ruleset. During cooldown the button is visually dimmed and shows a cooldown fill overlay. Inventory, equipped-slot, command, and hovered world-item buttons also use the same rules description data for hover tooltips.
The following attributes let intent buttons show specific mouse cursors when hovering or clicking an entity or item. They work in 3D, and also in 2D when [game].click_intents_2d = true:
- entity_cursor_id - The tile id for the mouse cursor when hovering above a character.
- entity_clicked_cursor_id - The tile id for the mouse cursor when clicking a character.
- item_cursor_id - The tile id for the mouse cursor when hovering above an item.
- item_clicked_cursor_id - The tile id for the mouse cursor when clicking an item.
Drag And Drop Slots
Buttons that represent inventory_index or equipped_slot automatically enable drag-and-drop unless you explicitly set drag_drop = false.
- Click/release on the same slot still triggers normal intent handling (for example
look). - Dragging an item and releasing on another slot moves or swaps items.
- Dropping onto an equipped slot is validated against the item's
slotattribute. - Incompatible equipment drops are ignored (for example
main_handitem intooff_handslot). - World items can also be dragged directly into slot widgets.
- Dragging an owned item out of a slot and onto terrain drops it into the world.
- Slot widgets can bind to different party members using
party.
[ui]
role = "button"
inventory_index = 0
party = "leader"
[ui]
role = "button"
equipped_slot = "main_hand"
party = "party.1"
[ui]
role = "button"
portrait = true
party = "leader"
Example #1
In this example, clicking the button causes the player to move forward.
[ui]
role = "button"
command = "control.forward"
# command = "intent.talk"
# command = "intent." # Walk / default targeting mode
# command = "rules.basic_attack"
# command_slot = "main.0" # resolved from the active player's class action bar
# party = "leader"
# inventory_index = 0
# equipped_slot = "main_hand"
# portrait = true
# text = "Start Game"
# font = "Tiny5-Regular"
# font_size = 28.0
# color = "#f2f2f2"
# background_color = "#111111cc"
# border_size = 1
# border_color = "#888888"
Use command = "intent." or intent = "" for a Walk button. This selects the normal map cursor and clears active targeting commands.
Command buttons resolve ruleset icons through [actions.<id>.ui], [intents.<id>.ui], and the shared [icons] catalog. Project textures can still override a button by assigning custom normal/selected/pressed/disabled textures on the widget itself.
If a button command matches a key in the active player's [input] table, the hover tooltip shows the shortcut.
Use command_slot = "main.0" / main.1 / main.2 and so on for class-driven action buttons. The slot first checks the active player for command_slot_main_0 or action_slot_main_0, then falls back to [classes.<Class>.action_bar] in the active ruleset.
For 2D action bars that sit on top of the game view, put a role = "deco" widget behind the buttons with layer = -1 and a semi-transparent color. Negative-layer deco widgets draw below screen-rendered controls, which lets the background dim the map without dimming the icons.
For start screens, use generic UI state bindings for choices. The selected class button stays highlighted because the button's value matches the current bind value. game.start reads start.class and start.name when spawning the player.
[ui]
role = "button"
bind = "start.class"
value = "Warrior"
selection = "single"
active = true
text = "Warrior"
Input Widgets
Use role = "input" for simple text entry on screens. Inputs write to generic UI state via bind / binding; game.start reads start.name.
[ui]
role = "input"
bind = "start.name"
text = "Edliron"
font = "Tiny5-Regular"
font_size = 24.0
color = "#f2f2f2"
background_color = "#111111cc"
border_color = "#888888"
border_size = 2
Example 2
This button:
- Shows the widget with the sector name of
Messages - Hides all widgets which starts with
Inventory. - Deactivate the button named
Show Inventory. - Sets its own state to
active.
[ui]
role = "button"
show = ["Messages"]
hide = ["Inventory*"]
deactivate = ["Show Inventory"]
active = true
Example 3
This button switches both rendering camera and player input mapping to first-person.
[ui]
role = "button"
camera = "firstp"
player_camera = "firstp"
# camera_target = "Game View"
Example 4
Spell intent button.
- In 3D, select it and click a character to cast at that target.
- In 2D, select it and cast via directional intent input (like other directional intents such as
look).
[ui]
role = "button"
intent = "spell"
spell = "Fireball"
deactivate = ["LookIntent", "UseIntent", "PickupIntent", "DropIntent"]
border_size = 1
border_color = "#ffff88"
Avatar Widgets
Use a widget with role = "avatar" to render an avatar preview in the UI.
This uses the same avatar colorization pipeline as runtime avatars and can optionally include equipped weapons.
UI Section
- avatar - Avatar name to preview (for example
"Human"). If omitted, the bound character avatar is used. - party - Optional binding target for the avatar widget. If omitted, Eldiron uses the current leader.
- animation - Optional animation name (for example
"Idle"). If omitted, the current avatar animation is used. - frame_index - Frame index to render (defaults to
0). - perspective - Optional direction:
front,back,left,right(defaults tofront). - show_weapons - Include equipped weapon overlays (
trueby default).
Example
[ui]
role = "avatar"
party = "leader"
# avatar = "Human"
animation = "Idle"
frame_index = 0
# perspective = "front"
# show_weapons = true
Party Binding
Screen widgets that show character-related content can bind to a party target with ui.party.
Supported values today:
leader- the default binding used by existing projectsparty.0,party.1, ... - bind by party index- a character
party_role - a character
name
This system is intentionally expandable. Current projects continue to work because omitted party still resolves to the leader/current player path.
Text Widgets
Text widgets display text on the screen and can include static content or dynamic placeholders for player or game data.
UI Section
- text: multiline string content; placeholders like
{PLAYER.CLASS},{PLAYER.RACE},{PLAYER.STR},{PLAYER.DEX},{PLAYER.FUNDS},{PLAYER.LEVEL},{PLAYER.EXP},{PLAYER.ATTACK},{PLAYER.ARMOR}, and start-screen placeholders like{START.CLASS}are replaced at runtime. - tab_width: optional tab stop width in pixels for
\talignment inside the text. - font: font family name.
- font_size: size of the font.
- spacing: line spacing.
- color: text color.
- align or horizontal_align: optional text alignment. Supported values are
left,center, andright. Lines containing tab stops stay left-aligned so stat tables remain predictable.
You can customize the font, size, line spacing, and color of the text.
Combat-related player placeholders:
PLAYER.CLASSis the player's rules class, falling back to the character template name.PLAYER.RACEis the player's rules race.PLAYER.ATTACKis the accumulatedDMGvalue across the player's equipped weapon slots.PLAYER.ARMORis the accumulatedARMORvalue across the player's equipped gear slots.PLAYER.LEVELresolves throughgame.level.PLAYER.EXPandPLAYER.EXPERIENCEresolve throughgame.experience.PLAYER.WEAPON.<ATTR>sums an attribute across the player's weapon slots.PLAYER.EQUIPPED.<ATTR>sums an attribute across all equipped items.PLAYER.ARMOR.<ATTR>sums an attribute across the player's gear slots.
The slot groups come from game.weapon_slots and game.gear_slots in Game / Settings.
Start-screen placeholders use generic UI state and work before the player has spawned:
START.NAMEis the currentstart.nameinput value.START.CLASSis the currentstart.classchoice.START.CLASS_ROLE,START.CLASS_ATTRIBUTES,START.CLASS_WEAPONS,START.CLASS_ARMOR,START.CLASS_ABILITIES,START.CLASS_SPELLS,START.CLASS_EQUIPMENT, andSTART.CLASS_INVENTORYare resolved from the active ruleset class.
Example
[ui]
role = "text"
text = """
Welcome, {PLAYER.CLASS}!
LVL:\t{PLAYER.LEVEL}\tEXP:\t{PLAYER.EXP}\tG:\t{PLAYER.FUNDS}
HP:\t{PLAYER.HP}/{PLAYER.MAX_HP}\tMP:\t{PLAYER.MP}/{PLAYER.MAX_MP}
STR:\t{PLAYER.STR}\tDEX:\t{PLAYER.DEX}
INT:\t{PLAYER.INT}\tWIS:\t{PLAYER.WIS}
ARM:\t{PLAYER.ARMOR}\tRES:\t{PLAYER.RESIST}\tPWR:\t{PLAYER.POWER}
May the stars guide you!
"""
font = "Tiny5-Regular"
font_size = 18.0
spacing = 2.0
tab_width = 110.0
color = "#aaaaaa"
Messages Widget
The Messages widget displays all incoming messages for the player in a scrollable list.
UI Section
- Messages are sent using the message command.
- Each message can include an optional category to pick a display color.
- If no category is specified, messages default to color #aaaaaa (override with default).
You can define custom colors for categories using keys in the widget's data section.
In the example below, messages with the "warning" category will appear in light red.
By default, messages are listed bottom-up (most recent at the bottom).
To change this to top-down, set top_down = true in the widget’s configuration.
Example
[ui]
role = "messages"
font = "Tiny5-Regular"
font_size = 18.0
spacing = 5
message_spacing = 8
background_color = "#00000099"
warning = "#ff8888"
default = "#ffffff"
Use handles to decide which message/choice streams a Messages widget displays. This lets you place one Messages widget as a log and another as a dialog overlay on top of the game view.
[ui]
role = "messages"
handles = ["dialogs"]
top_down = true
Supported handles are:
messages: Regularmessage(...)output.dialogs: TOML-authoreddialog(...)text and choices.multiple_choice: Script-authoredmultiple_choice(...)prompts and choices.offer_inventory: Vendor inventory offers fromoffer_inventory(...).
If handles is omitted, the widget displays all streams.
Messages widgets can optionally draw the sender character's existing portrait_tile_id on the left and wrap text to the right. This works for regular messages, dialogs, script multiple-choice menus, and inventory offers.
[ui]
role = "messages"
handles = ["dialogs"]
portrait = true
portrait_size = 64
portrait_gap = 12
Layout Notes
spacing: vertical spacing between wrapped lines inside one message blockmessage_spacing: vertical spacing between separate message blocksportrait_size: square portrait size in pixels whenportrait = trueportrait_gap: horizontal spacing between the portrait and textpress_to_continue: pauses output when the current page fills while more text is queued and shows a promptcontinue_prompt: text shown while waiting for the player to continuepause_blocks_input: blocks player input during message pauses, defaults totruescrollback: lets the player use the mouse wheel to review older messagesmax_messages: number of messages kept for scrollback, defaults to100command_input: shows a desktop-only command input line at the bottom of the widgetcommand_prompt: prompt text for command input, defaults to@command_prompt_color: command input color as#RRGGBBbackground: set totrueto draw a semi-transparent default backgroundbackground_color: optional widget background as#RRGGBBor#RRGGBBAA; setting this also enables the backgroundbackground_padding: expands the background rectangle outside the widget bounds
This is useful when long wrapped messages would otherwise run visually into the next message.
Pauses and Scrollback
Messages widgets can pause output automatically when the visible page fills and more queued text is waiting:
[ui]
role = "messages"
press_to_continue = true
continue_prompt = "Press Space"
pause_blocks_input = true
scrollback = true
max_messages = 200
Command Input
Enable command_input to add a desktop command line to the bottom of the Messages widget. It uses the same text command parser as text play, which makes it useful for testing ruleset actions directly in graphical play.
[ui]
role = "messages"
command_input = true
command_prompt = "@"
Click the command line or press Return to focus it. Example commands:
cast minor heal
cast holy light orc
take golden key
look orc
Scripts can also insert explicit pauses into the message stream. Use the pause category with an empty message to show the continue prompt, or with a number of seconds for a timed pause:
message(id(), "First line.", "")
message(id(), "", "pause")
message(id(), "Second line after player input.", "")
message(id(), "Wait for it...", "")
message(id(), "5", "pause")
message(id(), "Five seconds later.", "")
Multiple Choice
For displaying multiple choice messages (for example when a vendor offers his inventory), there are two more options:
[ui]
column_width = 30 # The width of the body column of the text
multiple_choice = "#ffff88" # The color for multiple choice items