How-To: Write Augment Tooltips

Every augment can describe itself to the player. You get a generated description for free, you can override the wording anywhere it reads badly, and the numbers stay live either way - an override never goes stale when a value changes, because the numbers come from the data, not from what you typed.

Where the tooltip's title comes from
Set Display Name on the ability's Display Data. That is the only place an ability's player-facing name lives - the contract is a shared schema asset, so a name there would title every ability that references it identically. Leave it blank and the title is simply empty; the class name (GA_Fireball_C) is never substituted. Titles are rendered as rich text too, so markup works there as well as in descriptions.

The three layers

LayerWhereWins over
GeneratedAutomaticNothing - it is the fallback.
Per-step overrideDescription Override on an action, listener or mutationThe generated clause for that one step.
Whole-augment overrideDescription on the augment asset - the settings row at the top of the Augment tabEverything. The generated text is discarded entirely.
Start by overriding one step, not the whole augment
A whole-augment Description replaces the generated text completely, so every future change to the augment silently stops being described. A per-step Description Override only replaces that step, and the rest keeps generating itself.
Where the augment's own settings live
Selecting a row in the Augment tab shows that row in the Details panel. The augment's own settings - slot category, target contract, priority, declared properties and the whole-augment Description - are on the settings row at the very top of the stack, which is also what the editor opens on.

Tokens

Write {PropertyName} anywhere in an override and it is replaced with that property's current value. Property names are the ones on the action, listener or mutation you are editing - the same names shown in the details panel, without spaces.

text
Chains to {MaxTargets} nearby foes within {Radius}cm.
-> Chains to 2 nearby foes within 600cm.
A misspelled token stays visible
An unrecognized token is printed verbatim - you will see a literal {Radiuss} in the Tooltip Preview rather than a blank space. That is deliberate: a typo you can see is a typo you can fix.

Finding nested values

Some values are not directly on the action. Apply Gameplay Effect keeps its magnitudes in a map keyed by SetByCaller tag, so the Amount you want is one level down. Tokens search inside automatically - a direct property always wins, and only if there is no match does it look deeper.

TokenFinds
{Radius}A property on the action itself.
{Amount}The first Amount anywhere inside it, including inside a map entry.
{Damage.Amount}When several entries exist, the one whose map key ends in Damage. Keys match on their last segment, so SetByCaller.Damage.Base is addressed as Base.
First match wins
With several candidates and no qualifying segment, the first one found wins - depth-first, in the order properties are declared. It is stable, but qualify the token when more than one could match.

Format specifiers

A colon after the name adds formatting. The right form depends on where the token sits in the sentence, which is why there is no single default - "Radius" at the start of a sentence and "radius" in the middle are both correct, in different places.

WrittenValuePrintsUse for
{Radius}600600The default - up to 2 decimals, trailing zeros trimmed.
{Radius:0}600.4600A whole number. Any digit 0-6 sets the decimal places exactly.
{Radius:2}600600.00Fixed decimals, padded.
{Chance:percent}0.3535%A stored 0-1 value written the way a player reads it.
{Chance:percent,0}0.12513%Percent with the decimals pinned. Order does not matter here.
{MaxTargets:spaced}ChainTargetsChain TargetsSplitting a CamelCase name (bound values print a name, not a number).
{MaxTargets:lower}ChainTargetschaintargetsMid-sentence.
{MaxTargets:upper}ChainTargetsCHAINTARGETSHeaders.
{MaxTargets:title}ChainTargetsChain TargetsStart of a sentence.
{MaxTargets:spaced,lower}ChainTargetschain targetsSpecifiers are comma-separated and apply left to right.
{Amount:style=Damage}600<Damage>600</>Rich-text markup. Put it last - it makes the value non-numeric, so a following percent or decimals spec finds nothing to parse.
Order matters when you combine them
Specifiers run left to right. {Chance:percent,0} gives 35% - percent first, then round. {Chance:0,percent} rounds 0.35 to 0 first and gives 0%. When in doubt put percent first.

Tokens only mutations have

TokenResolves to
{Target}The display name of whatever the mutation changes - the contract's Display Name for a property, or the attribute's name. Use this instead of typing the stat's name, so renaming it in the contract updates every description.
text
Sets your blade alight for {Value} seconds, burning {Target}.
-> Sets your blade alight for 5 seconds, burning Attack Power.

Referencing a gate

An event group's gate - its chance and minimum level - is not a property of any single action, so a step's override gets a second pass against the group it belongs to. {Chance} and {MinimumLevel} resolve there.

text
Chains to {MaxTargets} nearby foes. {Chance:percent} chance.
-> Chains to 2 nearby foes. 35% chance.

The generator adds no wording of its own

It will not insert connectives (, or and ), an On Hit: prefix, a 35% chance to prefix, or trailing periods. All of that is phrasing, and phrasing is yours - "Chains to 2 foes. 35% chance." is unreachable if the generator has already committed to "35% chance to chain to 2 foes". Clauses are joined with a single space and carry their own punctuation, so write the punctuation you want inside each override.

Rich text

These descriptions are FText that your widget renders - the plugin never draws them. So markup you type into an override reaches a UCommonRichTextBlock unchanged, and works today with no setup.

text
Deals {Amount:style=Damage} damage over {Duration:1}s.
-> Deals <Damage>600</> damage over 3.0s.

You can also write the tags by hand - "Chains to <Damage>{MaxTargets}</> foes." - if you want the markup around more than one token.

The generator never emits markup
Generated text is always plain. That means the only style rows your project has to define are the ones you named yourself, and nothing can warn about a missing row you never asked for. It also means an un-authored augment is unstyled - if you want a number colored, that step needs an override.
Preview it with your own styles
Set Project Settings > Crimson > Crimson Ability System > Tooltip Preview Style Set to the same FRichTextStyleRow table your UI reads, and the Augment Editor's preview renders with the game's own styles. Left unset, the preview shows markup literally - deliberately, so you can see your tags are there rather than trusting a look that would not match the game.
Images use a decorator, not the style set
A rich text block has two independent halves. Its style set styles text - <Damage>600</> resolves against a row named Damage. Its decorators emit inline widgets - <img id="Skull"/> resolves against a different table, through URichTextBlockImageDecorator. One table does not do the other's job, which is why the preview has two settings.
Style setImage decorator
Markup<Damage>600</><img id="Skull"/>
Row typeFRichTextStyleRowFRichImageRow, or CommonUI's FRichTextIconData
Preview settingTooltip Preview Style SetTooltip Preview Image Set

The image table's row type is read reflectively, so both shapes work - a Brush, or a ResourceObject plus ImageSize. An <img> whose id is not in the table renders as the literal tag in warning color, the same way an unrecognized {Token} stays visible.

Setting up colored text

  1. Create the table. Content Browser > Miscellaneous > Data Table, and pick RichTextStyleRow as the row structure.
  2. Add a row per style. The row name is the tag - a row called Damage is written <Damage>. Set its Font, Color, and any decoration on the row.
  3. Point your widget at it. On the Rich Text Block in your tooltip widget, set Text Style Set to that table.
  4. Use it. Write <Damage>600</> in a Description Override, or let a token do it: {Amount:style=Damage}.
text
DT_TooltipTextStyles (row structure: RichTextStyleRow)
Default <- SRichTextBlock falls back to this; worth adding
Damage Font: Bold 12, Color: (1.0, 0.35, 0.25)
Buff Font: Bold 12, Color: (0.45, 0.9, 0.5)
Description Override:
"Deals {Amount:style=Damage} damage."
-> Deals <Damage>600</> damage.
Both tables are project data
Neither table ships with the plugin, and neither is referenced by it at runtime - your widget owns them. The plugin only needs to know about them to render the preview, which is what the two Tooltip Authoring settings are for.

Setting up inline images

  1. Create the table. Data Table with RichImageRow as the row structure (or CommonUI's RichTextIconData if you are using UCommonRichTextBlock).
  2. Add a row per icon. The row name is the id. Set its Brush - texture plus image size.
  3. Attach the decorator. On an engine Rich Text Block, add RichTextBlockImageDecorator to Decorator Classes and set its Image Set to your table. With CommonUI instead, subclass UCommonUIRichTextData, put your table in its Inline Icon Set, and select that subclass in Project Settings > CommonUI.
  4. Use it - and mind the syntax, which differs between the two.
WidgetRow structureMarkup
URichTextBlock (engine)FRichImageRow<img id="Skull"/>
UCommonRichTextBlockFRichTextIconData<Skull/>
CommonUI uses a different tag for icons
CommonUI's decorator matches on the run name itself (CommonRichTextBlock.cpp tests RunParseResult.Name), so an icon row called Skull is written <Skull/> - there is no img run and no id key. Writing <img id="Skull"/> into a UCommonRichTextBlock renders nothing. The Augment Editor preview accepts both forms so it matches whichever widget you use.
text
DT_TooltipIcons (row structure: RichImageRow)
Skull Brush: T_Icon_Skull, Size: 16 x 16
Description Override, engine rich text block:
"Executes below <img id=\"Skull\"/> 20% health."
Description Override, CommonUI rich text block:
"Executes below <Skull/> 20% health."

Bound values print a name, not a number

A value bound to an ability property resolves to whatever is equipped at the time, so outside a live loadout there is no single number to print - it shows the property's name instead. The Augment Editor's Tooltip Preview resolves it for the previewed contract and shows both, as Chain Targets (3).

Reading it from a widget

Call Build Ability Tooltip (category Crimson | Ability | UI) with the ability. Break the result into Title, Description, Stats and Augments. Drive a list from Stats, and tint the rows where bModified is true - that is the flag telling you an augment changed the number.

Drawing it your own way

Build Ability Tooltip returns the whole structure, which suits a UI that draws the whole structure. For anything else, UCrimsonAbilityTooltipStatics (same Crimson | Ability | UI category) takes it apart rather than making you walk Stats yourself.

You wantCall
One number, bound to one property tagGet Ability Stat - resolves without building the rest of the tooltip
The rows for one category, or only the ones that changedFilter Stats with a Tooltip Stat Filter
One section widget per categoryGroup Stats By Category, ordered by Get Ability Stat Categories
Text and tint for a rowFormat Stat Value, Format Stat Delta, Get Stat Change Direction
One row per augment effect instead of a paragraphGet Augment Effect Lines For Ability
What changes if the player equips this augmentBuild Ability Tooltip With Augment, then Diff Tooltips
Two kinds of type
A stat's Value Type is its shape - Float, Int, Bool, Vector, or Attribute for an ability-scoped attribute adjustment. Its Categories are the semantic type: gameplay tags you author on the contract property (Stat.Offense, Stat.Utility) and which every stat line carries, so a panel asks for "the Offense rows" without knowing a single property tag.

Each property also carries a Display block - Suffix, As Percent, Decimals, Higher Is Better, Hide From Tooltip. That is what turns 8.5 into 8.5s, and what lets Get Stat Change Direction say a lower cooldown is an improvement without every widget keeping its own list of inverted stats.

Refresh on the delegate, not on tick
A tooltip built once keeps showing the build the player had when the panel opened. Bind On Ability Properties Changed on the ability system component and rebuild - it fires on server and clients alike when an augment is equipped or unequipped, when an augment or the ability is levelled, and when the augment container replicates.

Checking your work

The Augment Editor's Summary tab renders a Tooltip Preview using real numbers for the previewed contract at the toolbar's Preview Level - not placeholders. Change Preview Level and the numbers move with it. It assumes this augment alone is equipped, so a real loadout with several augments touching the same property will read differently - Build Ability Tooltip With Augment is the one that answers the loadout question.

See also

  • Concept: Augment System - what mutations, actions and listeners are
  • How-To: Add Your Own Augment Event - broadcasting events your game defines
  • API Reference - BuildAbilityTooltip, UCrimsonAbilityTooltipStatics and the tooltip structs