Widgets
Table of Contents generated with DocToc
- Sidebar Modification
- Widgets
- Styles
- Fields
- Clauses and conditions
- Variable ranges
- Variables
- Predefined widgets
Sidebar Modification
Most of the main CDDA sidebar is now moddable, meaning it is data-driven and can be customized simply by editing JSON files, without recompiling the game.
From the in-game Sidebar Options menu }
, select the “custom” layout to switch to a basic moddable theme built from the “custom_sidebar” widget defined in data/json/ui/sidebar.json
, with sections you can toggle or rearrange in-game according to your preference.
You can deeply customize the look of your sidebar widgets, by directly modifying the JSON that defines them in data/json/ui
, or by creating your own sidebar mod in data/mods
. This document is all about widgets: what they do, and how to use them.
Widgets
All “custom” sidebar UI elements are defined in objects called widgets. A widget can display a variety of player character attributes in numeric or text form, or as a bar graph of arbitrary width or height. A widget can also group other widgets together in a horizontal or vertical layout.
Widget instances are defined by JSON data, with the main game sidebar widgets and layouts being in data/json/ui/sidebar.json
. You may customize yours by editing this file, or by loading a mod that adds or modifies widget definitions (see Modding the sidebar).
For example, here is a widget to display the player character’s “Focus” attribute as a number:
{
"id": "focus_num",
"type": "widget",
"label": "Focus",
"var": "focus",
"style": "number"
}
And how it may look in-game:
Focus: 105
The label, var, and style fields define what info is shown, and how. Several other fields provide more detailed customization, depending on context.
Required fields
Two fields are required for all widgets:
field | description |
---|---|
id | Unique identifier for this widget, usually like “lowercase_with_underscores” |
type | Must be set to “widget” for all widget objects. |
NOTE For cleanliness and readability, many JSON examples in this document omit “id” and “type”, because they are always required. Assume they are implied in all examples, unless otherwise noted:
{
"id": "some_unique_id",
"type": "widget",
"//": "...and so on"
}
Optional fields
This table lists all other widget fields and what they do. Many are explained in more detail in the linked sections:
field | type | description | |
---|---|---|---|
arrange | string | For “layout” style, display child widgets as “rows”, “columns” or “minimum_columns”; for “graph style” draw vertical (“rows”) or horizontal (“columns”) | |
bodypart | string | For “bp_*” variables, body part id like “leg_r” or “torso” | |
separator | string | The string used to separate the label from the widget data. Children will inherit if this is not defined. Mandatory if style is “sidebar”. | |
padding | int | Amount of padding between columns for this widget. Children will inherit if this is not defined. Mandatory if style is “sidebar”. | |
colors | list of strings | Color names in a spectrum across variable range, or a single color for text widgets | |
breaks | list of integers | Color breaks as percentages in the variable range. Optional, overwrites default algorithm. | |
direction | string | Cardinal compass direction like “N” or “SE” | |
fill | string | For graph style, fill using ike “bucket” or “pool” | |
flags | list of strings | Optional toggles | |
height | integer | Maximum number of lines of text to take up | |
label | string or translation | Visible descriptor or heading | |
string | string or translation | Visible descriptor or heading. Used as a last resort by “text” style widgets. Mandatory if the widget has no clauses. | |
clauses | list of objects | Arbitrary conditional expressions mapped to colored text, symbols, or numbers | |
style | string | Sub-type or visual theme: “number”, “graph”, “text”, “layout” | |
symbols | string | For graph style, text characters for ascending values | |
var | string | Variable name from widget_var ; see Variables | |
widgets | list of strings | For “layout” and “sidebar” style, list of string IDs of child widgets. | |
width | integer | Total width in characters or symbols. | |
label_align and text_align | string | How to orient the label and value: “left”, “center”, or “right” | |
pad_labels | bool | Aligns values in layouts by padding to the longest label |
See Fields for details.
Styles
Two widget styles are for high-level organization and layout: sidebar and layout; three others are variable widgets for displaying specific information: number, graph, and text.
This section describes them in a top-down fashion.
Sidebar style
The highest-level widget is the “sidebar”, which represents the entire display region on the right (or left) edge of the screen. It includes a “width” in characters, a “label” displayed in the sidebar options menu, and a list of “widgets”, shown as sections that may be rearranged or toggled from the sidebar options menu.
These sub-widgets are typically layout widgets, with other widgets arranged inside them, but they could also be plain variable widgets, used for showing character attributes or other information.
Here is how a simple sidebar definition might look in JSON:
{
"id": "my_sidebar",
"style": "sidebar",
"width": 40,
"widgets": [
"sound_focus_move_layout",
"stats_layout"
]
}
Each widget in the “sidebar” will be associated with a panel_layout
instance in the code, which is what allows them to be toggled and rearranged like the classic sidebar sections.
You may define any number of “sidebar” widgets, each with their own width, label, and collection of sub-widgets and layouts.
Sidebar widgets aside, there are two major types of widget: variable widgets, showing some piece of information (with a label); and layout widgets, used for arranging other widgets in rows or columns.
We will look at layout widgets first, since they are easier to explain.
Layout style
Use widgets with “style”: “layout” to arrange child widgets in sidebar panels, giving widget ids in the “widgets” list field.
The arrangement of child widgets is defined by the “arrange” field, which may be “columns” (default) to array widgets horizontally, or “rows” to arrange them vertically, one widget per row. Normal columns will split their horizontal space as equally as possible. Whereas minimum_columns will take their exact amount of space (defaulting to space split like columns) with the last column in the row taking all remaining space.
[
{
"id": "sound_focus_move_layout",
"type": "widget",
"style": "layout",
"arrange": "columns",
"widgets": [ "sound_num", "focus_num", "move_num" ]
},
{
"id": "stats_layout",
"type": "widget",
"style": "layout",
"arrange": "columns",
"widgets": [ "str_num", "dex_num", "int_num", "per_num" ]
},
{
"id": "sound_focus_move_stats_layout",
"type": "widget",
"style": "layout",
"arrange": "rows",
"widgets": [
"sound_focus_move_layout",
"stats_layout"
]
}
]
The above might yield:
Sound: 8 Focus: 105 Move: 120
Str: 8 Dex: 9 Int: 7 Per: 11
These layout widgets can be nested to produce web-style layouts:
[
{
"id": "overmap_5x5",
"type": "widget",
"var": "overmap_text",
"style": "text",
"width": 5,
"height": 5,
"flags": [ "W_LABEL_NONE" ]
},
{
"id": "location_text_layout",
"type": "widget",
"style": "layout",
"arrange": "rows",
"widgets": [ "lighting_desc", "moon_phase_desc", "wind_desc", "env_temp_desc" ]
},
{
"id": "layout_location_columns",
"type": "widget",
"style": "layout",
"arrange": "columns",
"label": "Location",
"widgets": [ "overmap_5x5", "location_text_layout" ],
"flags": [ "W_LABEL_NONE" ]
}
]
The above would produce something like:
FFF.. Lighting: bright
FF... Moon: Waxing crescent
FF..P Wind: Light Breeze =>
F...| Temperature: 16C
F...|
Where do all these numeric widgets and their values come from? These are variable widgets, discussed next.
Variable widgets
Variable widgets define a “var” field, with the name of a predefined widget variable. This tells the widget what information it should show. Most of the time, these are attributes of the player character, but they can also be attributes of the world, environment, or vehicle where they are.
For example, a widget to show the current STR stat would define this “var”:
{
"var": "stat_str"
}
And a widget to show the HP of the right arm would define “var” and “bodypart” like so:
{
"var": "bp_hp",
"bodypart": "arm_r"
}
Some widgets can take advantage of multiple “bodyparts” like so:
{
"bodyparts": [ "head", "torso", "arm_l", "arm_r" ]
}
See Variables for a list of available “var” values.
Number style
The simplest and usually most compact widget for displaying a value, “style”: “number” appears as a label with an integer number.
{
"style": "number",
"label": "Focus"
}
Result:
Focus: 100
The numeric value comes from the given “var”, displayed as a decimal integer. By default it will be plain gray, but providing a spectrum of colors, will colorize the number based on the variable range of the given “var”.
See Number widgets for several pre-defined numeric widgets you can use or extend.
Graph style
The graph shows an arrangement of symbols. It has two important parameters:
width
: how many characters wide is the graphsymbols
: single-character strings to map to 0-N
Given a graph of width 3 with two symbols, “-“ and “=”:
{
"width": 3,
"symbols": "-="
}
The first symbol is the zero or blank filler symbol, and remaining symbols are cycled through to fill the width of the graph. This simple graph uses the “-“ symbol for its zero-filler, leaving one symbol “=” that expands to fill the width from left to right. The graph has four possible states - the all-zero state, and one state as each position up to “width” (3) is filled by the “=” symbol:
0: -
1: =
--- 000
=-- 100
==- 110
=== 111
The simplest possible graph is one character wide, with one symbol. It always shows the same value, so is not very useful:
{
"width": 1,
"symbols": "X"
}
The simplest useful graph is one character wide, with two symbols:
{
"width": 1,
"symbols": "XO"
}
Such a graph would effectively compress its variable’s value range to a simple threshold (off/on), and could be used to create a single-character “pain alarm” or “loud sound” widget, for example.
Returning to the example with 3 width, and using three symbols, “-“, “=”, and “#”, we may define:
{
"width": 3,
"symbols": "-=#"
}
Here, the number of states increases to 7 - the zero state with all “-“, plus two layers of filling with “=” and “#”:
0: -
1: =
2: #
--- 000
=-- 100
#-- 200
#=- 210
##- 220
##= 221
### 222
See the fill and colors fields for more ways to customize the graph, and see Variable ranges for details on how the minimum and maximum extents of the graph are determined.
Also see Graph widgets for some predefined ones you can use or extend.
Vertical graphs
By setting the arrange
property to rows
, graphs can be displayed vertically. For vertical graphs, height
should be used instead of width
.
{
"arrange": "rows",
"height": 5,
"width": 1,
"symbols": ".▁▂▃▄▅▆▇█"
}
Note: As with other multi-line widgets, the
width
needs to be set to achieve narrow packing.
Vertical graphs do not work well with the label
property. Best to disable labels, and make a custom text
style widget to place above or below bars.
Text style
Text style widgets display text. They can be very powerful, but are also pretty complex.
The simplest text widget is one that displays static text using the string
field. If a text widget does not have any clauses or a var
field, it must have the string
field. The widget below displays a single dot.
{
"id": "lcom_spacer",
"type": "widget",
"style": "text",
"string": ".",
"flags": [ "W_LABEL_NONE" ]
}
In the vast majority of cases, text widgets will display text conditionally using clauses. These clauses use dialogue conditions to determine what text to show and in what color. The below widget is a prime example of a text widget, and is used to display a player’s thirst level.
{
"id": "thirst_desc_label",
"type": "widget",
"label": "Thirst",
"style": "text",
"clauses": [
{
"id": "parched",
"text": "Parched",
"color": "light_red",
"condition": { "math": [ "u_val('thirst') > 520" ] }
},
{
"id": "dehydrated",
"text": "Dehydrated",
"color": "light_red",
"condition": {
"and": [
{ "math": [ "u_val('thirst') > 240" ] },
{ "math": [ "u_val('thirst') <= 520" ] }
]
}
},
{
"id": "very_thirsty",
"text": "Very thirsty",
"color": "yellow",
"condition": {
"and": [
{ "math": [ "u_val('thirst') > 80" ] },
{ "math": [ "u_val('thirst') <= 240" ] }
]
}
},
{
"id": "thirsty",
"text": "Thirsty",
"color": "yellow",
"condition": {
"and": [
{ "math": [ "u_val('thirst') > 40" ] },
{ "math": [ "u_val('thirst') <= 80" ] }
]
}
},
{
"id": "neutral",
"text": "",
"color": "white",
"condition": {
"and": [
{ "math": [ "u_val('thirst') >= 0" ] },
{ "math": [ "u_val('thirst') <= 40" ] }
]
}
},
{
"id": "slaked",
"text": "Slaked",
"color": "green",
"condition": {
"and": [
{ "math": [ "u_val('thirst') >= -20" ] },
{ "math": [ "u_val('thirst') < 0" ] }
]
}
},
{
"id": "hydrated",
"text": "Hydrated",
"color": "green",
"condition": {
"and": [
{ "math": [ "u_val('thirst') >= -60" ] },
{ "math": [ "u_val('thirst') < -20" ] }
]
}
},
{
"id": "turgid",
"text": "Turgid",
"color": "green",
"condition": { "math": [ "u_val('thirst') < -60" ] }
}
]
},
See Text widgets for a variety of predefined text widgets you can use or extend.
Fields
label
The “label” is the word or phrase that appears in the UI to identify this widget. For “number”, “graph”, or “text” widgets, the label is shown to the left of the the value unless the W_LABEL_NONE
flag is given. For “layout” widgets, the label may appear as the name of a section within the sidebar.
Labels may be a plain string:
{
"id": "sound_num",
"label": "Sound",
"var": "sound"
}
Or it may define a translatable string object, as commonly seen in item names:
{
"id": "place_name",
"label": { "str": "Place", "ctxt": "location" },
"var": "place_text"
}
The English word “place” can be a verb, to put something down. Here “place” is a noun meaning a location. The “ctxt” part provides this context to translators so they can choose the most appropriate words in other languages.
See the Translatable strings section of JSON_INFO.md for more on how these work.
string
If you have a text
style widget that has no other options for what to display, it must have a string
field to display instead. This will cause the widget to display a static string.
fill
For “graph” widgets with more than two symbols, different ways of filling up the graph become possible. The method is specified with the “fill” field. By default the “bucket” fill method is used, but there is also a “pool” method described below.
With “bucket” fill, positions are filled like a row of buckets, using all symbols in the first position before beginning to fill the next position. This is like the classic 5-bar HP meter.
{
"width": 5,
"symbols": ".\\|",
"fill": "bucket"
}
Result:
..... 00000
\.... 10000
|.... 20000
|\... 21000
||... 22000
||\.. 22100
|||.. 22200
|||\. 22210
||||. 22220
||||\ 22221
||||| 22222
Using “pool” fill, positions are filled like a swimming pool, with each symbol filling all positions before the next symbol appears.
{
"width": 5,
"symbols": "-=#",
"fill": "pool"
}
Result:
----- 00000
=---- 10000
==--- 11000
===-- 11100
====- 11110
===== 11111
#==== 21111
##=== 22111
###== 22211
####= 22221
##### 22222
The total number of possible graphs is the same in each case, so both have the same resolution.
style
The “style” field says what kind of info this widget shows or how it will be rendered. It may be:
number
: Show value as a plain integer numbergraph
: Show a bar graph of the value with colored text characterstext
: Show text from a*_text
variablelayout
: Layout container for arranging other widgets in rows or columnssidebar
: Special top-level widget for defining custom sidebars, having several layouts
“style” can also be symbol
or legend
, which are specific to clauses.
direction
Widgets using compass_text
expect the additional fields direction
and width
to identify (respectively) the cardinal direction and number of creatures displayed:
{
"var": "compass_text",
"direction": "N",
"width": 6
}
compass_legend_text
makes use of the “height” field (see below), which tells the display function to reserve that many lines for the compass legend:
{
"var": "compass_legend_text",
"height": 3
}
height
Some widgets can make use of multiple lines by specifying the "height"
field, which reserves vertical space in the sidebar. Display functions can make use of this extra space to render multi-line widgets.
Warning: implementation details ahead.
The max width and height available for a widget is passed to its display::
function through widget::color_text_function_string()
. The display function can use this data to format the widget text as a series of lines delimited by a newline (\n
).
The formatted string is passed to widget::show()
and widget::layout()
, which format each line individually for drawing in widget::custom_draw_multiline()
.
Adding new multi-line-capable widgets involves ensuring the new display function formats the widget’s text according to the available width and height.
Some multi-line widgets can dynamically adjust their height based on how many lines they are using. To enable this behavior, add the W_DYNAMIC_HEIGHT
flag to the widget (ex: see the compass legend).
text_align and label_align
The widget’s label and text/value can be aligned using the label_align
and text_align
respectively. This is useful for widgets in “rows”-style layouts where the labels are different lengths, as the text can be aligned along a common vertical across the column:
{
"label_align": "right",
"text_align": "left"
}
Temp: Mildly cold
Comfort: Cozy
Pain: No Pain
Values may be “left”, “right”, or “center”. The default is “left” alignment for both labels and text.
pad_labels
In layouts, values can be aligned to match the longest label:
Mood: :)
Morale: 95
Activity: Brisk
pad_labels
can be used on layouts to enable/disable label padding of child widgets. It can also be used on non-layout widgets, to disable alignment individually.
{
"pad_labels": true
}
Defaults to true
for row layouts and all non-layout widgets. Defaults to false
for column layouts.
colors
Widgets with “number” or “graph” style may define “colors”, which will be used as a spectrum across the widget’s values (var_min
to var_max
), applying the appropriate color to each value based on the Variable range of the specified “var”.
For example, a lower movement number (move cost) is better, while higher numbers are worse. Around 500 is quite bad, while less than 100 is ideal. This range might be colored with green, white, and red, given in a “colors” list:
{
"id": "move_num",
"type": "widget",
"label": "Move",
"var": "move",
"style": "number",
"colors": [ "c_green", "c_white", "c_red" ]
}
Color names may be any of those described in COLOR.md. You can also see the available colors in-game from the “Settings” menu, under “Colors”.
Graphs can be colorized in the same way. For example, the classic stamina graph is a 5-character bar, a dark green |||||
when full. As stamina diminishes, the bar’s color goes to light green, yellow, light red, and red. Such coloration could be represented with “colors” like so:
{
"id": "stamina_graph_classic",
"type": "widget",
"label": "Stam",
"var": "stamina",
"style": "graph",
"width": 5,
"symbols": ".\\|",
"colors": [ "c_red", "c_light_red", "c_yellow", "c_light_green", "c_green" ]
}
The number of colors you use is arbitrary; the range of possible values will be mapped as closely as possible to the spectrum of colors, with one exception - variables with a “normal” value or range always use white (c_white
) when the value is within normal.
The color scale can be further customized using breaks
.
Widgets with “text” style can specify a single-element list of colors to overwrite the text color. Here is an example of colored place test:
{
"id": "place_green",
"type": "widget",
"style": "text",
"label": "Place",
"var": "place_text",
"colors": [ "c_green" ]
}
breaks
Color scales for widgets with “number” or “graph” style can be further customized by defining breaks
. There must be one break less than the number of colors.
For example, you may want the stamina bar to turn red at much higher values already, as a warning sign:
{
"id": "stamina_graph_classic",
"type": "widget",
"label": "Stam",
"var": "stamina",
"style": "graph",
"width": 5,
"symbols": ".\\|",
"colors": [ "c_red", "c_light_red", "c_yellow", "c_light_green", "c_green" ],
"breaks":[ 50, 70, 90, 95 ]
}
Breaks are percentages in the spectrum across the widget’s values (var_min
to var_max
). So, 0 stands for var_min
and 100 for var_max
. Values <0 and >100 are allowed.
flags
Widgets can use flags to specify special behaviors:
{
"id": "my_widget",
"type": "widget",
"style": "text",
"label": "My Widget",
"var": "my_widget_var",
"flags": [ "W_LABEL_NONE", "W_DISABLED_BY_DEFAULT" ]
}
Here are the flags that can be included:
Flag id | Description |
---|---|
W_LABEL_NONE | Prevents the widget’s label from being displayed in the sidebar |
W_DISABLED_BY_DEFAULT | Makes this widget disabled by default (only applies to top-level widgets/layouts) |
W_DISABLED_WHEN_EMPTY | Automatically hides this widget when the widget’s text is empty |
W_DYNAMIC_HEIGHT | Allows certain multi-line widgets to dynamically adjust their height |
W_NO_PADDING | Prevents the sidebar from doing any sort of whitespace-based alignment. All widgets are packed as tightly as possible. Use this flag only if you plan to align things yourself. |
Clauses and conditions
Widgets can take advantage of “clauses” - definitions for what text/values to display and how to display them. These take the form of a nested object containing several optional fields:
{
"id": "bp_status_indicator_template",
"type": "widget",
"style": "text",
"clauses": [
{ "id": "bitten", "text": "bitten", "sym": "B", "color": "yellow", "condition": "..." },
{ "id": "infected", "text": "infected", "sym": "I", "color": "pink", "condition": "..." },
{ "id": "bandaged", "text": "bandaged", "sym": "+", "color": "white", "condition": "..." },
{ "id": "some_var", "text": "<color_red>The some_var</color> is <global_val:some_var>", "parse_tags":true , "condition": "..." },
]
}
In the above example, the widget is simply used as a template for other widgets to copy-from
, which provides text and color definitions for different bodypart status conditions.
JSON Field | Description |
---|---|
id | An optional identifier for this clause |
text | Translated text that may be interpreted and displayed in the widget. |
sym | A shortened symbol representing the text. |
color | Defines the color for the text derived from this “clause”. |
value | A numeric value for this “clause”, which may be interpreted differently based on the context of the parent widget. |
widgets | For “layout” style widgets, the child widgets used for this “clause”. |
condition | A dialogue condition (see Dialogue conditions) that dictates whether this clause will be used or not. If the condition is true (or when no condition is defined), the clause can be used to its text/symbol/color in the widget’s value. |
parse_tags | default false. If true, parse custom entries in text before displaying it. This can be used to display global_val or u_val.(see Special Custom Entries for details) You can also use <color_XXX></color> to modify the color of your text. |
Conditions
Widget clauses and conditions can be used to define new widgets completely from JSON, using dialogue conditions. By omitting the widget’s var
field, the widget is interpreted as either a “text”, “number”, “symbol”, or “legend” depending on the given style
. The widget will evaluate each of its clauses to determine which ones to draw values from:
Widget style | Clause field used | Details | Example |
---|---|---|---|
"number" | "value" | Lists values as comma-separated-values from all clauses that have true conditions. | Next threshold: 30, 40, 55 |
"text" | "text" | Lists text as comma-separated-values from all clauses that have true conditions. | TORSO: bleeding, broken, infected |
"symbol" | "sym" | Lists syms sequentially from all clauses that have true conditions. | TORSO: b%I |
"legend" | "sym" and "text" | Lists syms and text in a paragraph format, with spaces between pairs, from all clauses that have true conditions. | b bleeding % broken I infected |
Widgets using the legend
style can be multiple lines high using a height
> 1 (and optionally, the W_DYNAMIC_HEIGHT
flag), so that the generated list can span the given vertical space.
Some conditions can be specific to certain bodyparts. In order to simplify clauses, these conditions can pull from the parent widget’s bodypart
field (or bodyparts
field if defining multiple). This allows the same clauses to be copy-from
‘d to multiple widgets, and each widget can display the clauses depending on whether its bodypart(s) passes the condition (assuming the condition relies on a bodypart).
Default clause
Widgets can define a default clause that will be used if none of the clauses in the clauses
array pass their conditions:
{
"id": "observ_widget",
"type": "widget",
"style": "text",
"label": "Observation",
"clauses": [
{
"text": "Good!",
"color": "light_green",
"condition": { "u_has_trait": "EAGLEEYED" }
},
{
"text": "Bad!",
"color": "light_red",
"condition": { "u_has_trait": "UNOBSERVANT" }
}
],
"default_clause": {
"text": "Neutral!",
"color": "white"
}
}
In the example above, the widget would print out the following text:
Player has trait | Widget text |
---|---|
Scout | Observation: Good! |
Topographagnosia | Observation: Bad! |
- | Observation: Neutral! |
Variable ranges
Widgets using a numeric “var” (those without a _text
suffix) have a predetermined absolute range (minimum and maximum), as well as a predetermined normal value or range. These limits are not customizable in widget JSON, but knowing about them will make it easier to understand how “graph” widgets are drawn, and how the “colors” list is mapped to the variable’s numeric range.
Within the code, these three widget
class attributes store the variable range info:
var_norm
: Range (minimum, maximum) of normal or baselinevar
valuesvar_min
: Value ofvar
mapped to the zero-point of graphs, and the lowest-index colorvar_max
: Value ofvar
mapped to the full-point of graphs, and the highest-index color
All these values are integer numbers only, not floating-point numbers. They may be negative.
The var_norm
range defines what value(s) of var
are considered normal, average, or baseline. For a character starting with 9 STR, their var_norm
for the stat_str
variable will be set to (9, 9)
. When the character’s STR is in the normal range, it will be displayed in white.
Usually, var_min
is simply 0, but some variables such as hidden health have a negative minimum value (-200 in this case). When using “colors”, the var_min
value is mapped to the first color. This is not necessarily the absolute minimum value that the variable can have; it is only the minimum value displayable on a graph, and below which the color stays fixed at the first color.
All widgets have some positive var_max
value, again depending on what var
is being displayed. This helps graph widgets know whether they must show values up to 7000 or more (like stamina) or only up to 100 or 200 (like focus). It also determines the value mapped to the last color in “colors”, if given. Again, this is not an absolute maximum; “number” widgets will continue to display numbers far in excess of the var_max
, but “graph” widgets will stop increasing at this value, and the color will stay at the last color.
These ranges may change dynamically during gameplay. For instance, as cardio fitness increases from day to day, the var_max
of corresponding cardio widgets must reflect this. Other variables with a potentially dynamic var_max
include “stamina”, “mana”, and “bp_hp”.
Likewise, when a character’s STR stat increases from a mutation, the var_norm
of corresponding widgets must adjust. Variables using “var_norm” include the stat attributes “stat_str”, “stat_dex”, “stat_int”, and “stat_per”.
Variables
Below are most of the available widget_var
values and what they mean. See the widget_var
list in widget.h
for the definitive list of available variables.
Many vars are numeric in nature. These may use style “number” or style “graph”. Some examples:
Numeric variables
var | description |
---|---|
cardio_acc | Cardio accumulator, integer |
cardio_fit | Cardio fitness, integer near BMR |
sleepiness | tiredness, 0-600+ |
focus | focus level, 0-100+ |
health | Current hidden health value, -200 to +200 |
mana | available mana, 0-MAX_MANA |
morale_level | morale level, -100 to +100 |
move | movement counter, 0-100+ |
move_remainder | remaining moves for the current turn, 0-9999+ |
pain | perceived pain, 0-80+ |
sound | sound, 0-20+ |
speed | speed, 0-500+ |
stamina | stamina reserves, 0-MAX (approx. 8700) |
stat_dex | dexterity stat, 0-20+ |
stat_int | intelligence stat, 0-20+ |
stat_per | perception stat, 0-20+ |
stat_str | strength stat, 0-20+ |
weariness_level | weariness level, 0-6+ |
Variables with a bp_
prefix refer to body part variables, and require a “bodypart” field. These variables have separate values for each part of the body, and include:
var | description |
---|---|
bp_hp | hit points of given “bodypart”, like “arm_l” or “torso”, 0-MAX_HP |
bp_encumb | encumbrance given “bodypart”, 0-50+ |
bp_warmth | warmth of given “bodypart”, 0-10000 |
bp_wetness | wetness of given “bodypart”, 0-100+ |
In the widget.cpp
code, get_var_value
returns the numeric value of the widget’s “var” variable, which in turn is used for rendering numeric widgets as well as graphs of that value. Graphs are rendered with reference to the maximum value for the variable; see Variable ranges.
Text variables
Some vars refer to text descriptors. These must use style “text”. Examples:
var | description |
---|---|
activity_text | Activity level - “None”, “Light”. “Moderate”, “Brisk”, “Active”, “Extreme” |
bp_outer_armor_text | Item name and damage bars of armor/clothing worn on the given “bodypart” |
compass_legend_text | (multiline) A list of creatures visible by the player, corresponding to compass symbols |
compass_text | A compass direction (ex: NE), displaying visible creatures in that direction |
date_text | Current day within season, like “Summer, day 15” |
env_temp_text | Environment temperature, if thermometer is available |
mood_text | Avatar mood represented as an emoticon face |
move_mode_letter | Movement mode - “W”: walking, “R”: running, “C”: crouching, “P”: prone |
move_mode_text | Movement mode - “walking”, “running”, “crouching”, “prone” |
overmap_loc_text | Overmap coordinates, same as shown in the lower corner of overmap screen |
overmap_text | (multiline) Colored text rendering of the local overmap; may define “width” and “height” |
pain_text | “Mild pain”, “Distracting pain”, “Intense pain”, etc. |
place_text | Location place name |
power_text | Bionic power available |
safe_mode_text | Status of safe mode - “On” or “Off”, with color for approaching turn limit |
safe_mode_classic_text | Status of safe mode - “SAFE”, with color for approaching turn limit |
style_text | Name of current martial arts style |
sundial_text | Current position of the Sun/Moon in the sky |
time_text | Current time - exact if clock is available, approximate otherwise |
veh_azimuth_text | Heading of vehicle in degrees |
veh_cruise_text | Target and actual cruising velocity, positive or negative |
veh_fuel_text | Percentage of fuel remaining for current vehicle engine |
weariness_text | Weariness level - “Fresh”, “Light”, “Moderate”, “Weary” etc. |
weary_malus_text | Percentage penalty affecting speed due to weariness |
weather_text | Weather conditions - “Sunny”, “Cloudy”, “Drizzle”, “Portal Storm” etc. |
wielding_text | Name of current weapon or wielded item |
wind_text | Wind direction and intensity |
Custom variables
Variable widgets with var custom
can show any variable object or math expression. These widgets require a separate custom_var
field to specify the variable object or math expression in question, like so:
{
"var": "custom",
"custom_var": {
"value": { "var_val": "name" } OR { "math": [ ... ] },
"range": [ ... ]
}
}
The range
field requires 2 to 4 elements, each of which can be an integer, variable object, or math expression. The first element specifies the minimum value, the middle element(s) specify the normal range, and the last element specifies the maximum value, as descibed in Variable ranges.
All custom variables are numeric and must use style “number” or style “graph”.
Predefined widgets
Many widgets for numbers, text, graphs, and layouts are already defined in data/json/ui/
, and you can save time customizing your sidebar by using these existing components. This section includes a list and demo/mockup of many of them.
Number widgets
Numerical widget ids typically have a _num
suffix.
id | example |
---|---|
cardio_fit_num | Cardio Fit: 1750 |
focus_num | Focus: 100 |
health_num | Health: -20 |
morale_num | Morale: 95 |
move_cost_num | Move cost: 300 |
move_num | Move count: 150 |
pain_num | Pain: 15 |
sound_num | Sound: 8 |
speed_num | Speed: 100 |
stamina_num | Stamina: 8714 |
str_num | Str: 8 |
dex_num | Dex: 8 |
int_num | Int: 8 |
per_num | Per: 8 |
Graph widgets
Graph widget ids typically have a _graph
suffix.
id | example |
---|---|
stamina_graph | Stamina: \|\|\|\|\|\|\|\|\|\| |
stamina_graph_classic | Stam: \|\|\|\|\| |
hp_head_graph | HEAD: \|\|\|\|\| |
hp_torso_graph | TORSO: \|\|\|\|\| |
hp_left_arm_graph | L ARM: \|\|\|\|\| |
hp_right_arm_graph | L ARM: \|\|\|\|\| |
hp_left_leg_graph | L LEG: \|\|\|\|\| |
hp_right_leg_graph | L LEG: \|\|\|\|\| |
Text widgets
Text widget ids typically have a _desc
suffix.
id | example |
---|---|
activity_desc | Activity: Moderate |
date_desc | Date: Summer day 25 |
env_temp_desc | Temperature: 65F |
sleepiness_desc | Rest: Tired |
health_desc | Health: Good |
hunger_desc | Hunger: Satisfied |
lighting_desc | Lighting: Bright |
mood_desc | Mood: :-) |
move_count_mode_desc | Move: 100(W) |
pain_desc | Pain: Unmanageable pain |
place_desc | Place: Evac Shelter J-38 |
power_desc | Bionic Power: 250mJ |
style_desc | Style: Brawling |
time_desc | Time: 10:45:32 am |
weary_malus_desc | Weary Malus: +10% |
weather_desc | Weather: Sunny |
weight_desc | Weight: Overweight |
wind_desc | Wind: <= Calm |
Layout widgets
Layout widget ids typically have a _layout
suffix. Complex layouts may be composed of simpler layouts, most often by building single-line layouts in “columns” first, then arranging several “columns” layouts into a multi-line layout using a “rows” arrangement.
Single-line layouts
This table gives some examples of single-line “columns” layouts:
id | example |
---|---|
hitpoint_graphs_top_layout | L ARM: \|\|\|\|\| HEAD: \|\|\|\|\| R ARM: \|\|\|\|\| |
hitpoint_graphs_bottom_layout | L LEG: \|\|\|\|\| TORSO: \|\|\|\|\| R LEG: \|\|\|\|\| |
hitpoints_head_torso_layout | HEAD: \|\|\|\|\| TORSO: \|\|\|\|\| |
hitpoints_arms_layout | L ARM: \|\|\|\|\| R ARM: \|\|\|\|\| |
hitpoints_legs_layout | L LEG: \|\|\|\|\| R LEG: \|\|\|\|\| |
mood_focus_layout | Mood: :-) Focus: 100 |
safe_sound_layout | Safe: Off Sound: 15 |
sound_sleepiness_focus_layout | Sound: 15 Fatigue: Fresh Focus: 100 |
sound_focus_layout | Sound: 15 Focus: 100 |
stats_layout | Str: 9 Dex: 8 Int: 10 Per: 7 |
Multi-line layouts
Below are examples of multi-line “rows” layouts:
hitpoints_all_graphs_layout
Combines hitpoint_graphs_top_layout
and hitpoint_graphs_bottom_layout
into a single layout showing all body part hit points:
L ARM: ||||| HEAD: ||||| R ARM: |||||
L LEG: ||||| TORSO: ||||| R LEG: |||||
hitpoints_all_narrow_graphs_layout
Alternative hitpoint graph, better for narrower sidebars. Combines hitpoints_head_torso_layout
, hitpoints_arms_layout
, and hitpoints_legs_layout
HEAD: ||||| TORSO: |||||
L ARM: ||||| R ARM: |||||
L LEG: ||||| R LEG: |||||
compass_all_layout
Full compass rose showing colored symbols for nearby monsters, along with a legend for what monster names belong to each symbol.
NW: zZZ N: a NE: ZZ
NW: Z E: da
SW: ddd S: SE: zZZZ
a 2 wasps d 5 zombie dogs
Z 6 zombies z 2 zombie cops
Modding the Sidebar
One great advantage of a data-driven, JSON-based sidebar is that it can be customized with mods. A mod may extend the main “custom” sidebar with mod-specific widgets, or define an entirely new sidebar if desired.
For example, the Magiclysm mod extends the main custom sidebar with two optional mana widgets. It does this in a JSON file in the mod directory, data/mods/Magiclysm/ui/sidebar.json
, by defining a few custom widgets, then using “copy-from” and “extend” on the custom sidebar object:
[
{
"copy-from": "custom_sidebar",
"type": "widget",
"id": "custom_sidebar",
"//": "Extend the custom sidebar with Magiclysm-specific sections",
"extend": { "widgets": [ "current_max_mana_nums_layout", "mana_graph_layout" ] }
}
]
These two extra widgets, “current_max_mana_nums_layout” and “mana_graph_layout”, will be appended to the custom sidebar sections whenever a game with the Magiclysm mod is loaded.