Ability Book

Goal: give the player a spellbook - every gameplay ability they have been granted, sorted into tabs, dragged onto an action bar slot. This is the source panel an action bar needs before anyone can see it work: without it, abilities only reach a bar by auto-place on grant.

Requires every Crimson plugin
CrimsonCore is free to download, but compiles only with all Crimson plugins installed. The ability book reads CrimsonActionBar's ability provider and CrimsonAbilitySystem's display data, so both must be enabled.
Prerequisites
A player with an ability system and some granted abilities (Character Pipeline), and an action bar on the player controller with a layout definition whose Providers array contains a Gameplay Abilities provider (CrimsonActionBar -> Quick Start). The book lists what that provider offers; with no provider it logs a warning and stays empty.

How it decides what to show

Nothing in the book is hard-coded to "active" or "passive". An ability declares its own type on its Ability Tags (the asset tags on the ability's Class Defaults). CrimsonActionBar's ability provider already copies those onto every entry it offers, so the screen matches them directly - there is no registry, no data asset and no code between the two.

The screen owns two gameplay tag containers. AbilityTypeFilter is the list of types to show, and each tag in it becomes one tab, in the order you authored them. DraggableAbilityTypes is the subset the player may actually bind; leaving it empty means everything is draggable.

TypeRole
UCrimsonCoreAbilityBookScreenThe screen, on UCrimsonActivatableWidget. Gathers from the bar's own ability provider, buckets entries by type, registers one tab per type and re-feeds a single tile view when the tab changes. Rebuilds when an ability is granted or removed, and keeps the player on the tab they were reading.
UCrimsonCoreAbilityBookEntryObjectOne tile's list item: the FCrimsonBindableEntry plus the tab it belongs to and whether it may be dragged. Draggability is the screen's decision, so it travels with the item instead of being re-derived per tile.
UCrimsonCoreAbilityBookEntryWidgetOne tile, on UCrimsonButtonBase + IUserObjectListEntry. Loads its own icon, and on drag produces a UCrimsonSlotBindingDragDropOperation - the operation an action bar slot already understands.
Why a passive is listed but not draggable
A passive ability has no gameplay-event trigger tag, so a passive sitting on an action bar is a button that does nothing when pressed. Showing it and refusing the drag is the honest version of that. A non-draggable tile is deliberately not disabled - it still hovers, focuses and shows a tooltip, because a grayed-out tile reads as broken rather than as intentional.

1. Tag your abilities

Open each ability the book should list and add its type to Ability Tags in Class Defaults. Ability.Type.Active and Ability.Type.Passive ship in CrimsonCore's tag list; a project is free to define a finer vocabulary, because matching is hierarchical - a tab for Ability.Type.Active also collects Ability.Type.Active.Instant.

Abilities you leave untagged never appear, which is the point: movement, camera and UI abilities are granted like any other and have no business in a spellbook.

Open the ability Blueprint, Class Defaults, find Ability Tags (under Tags), and add Ability.Type.Active or Ability.Type.Passive.

Verify
Nothing to see yet - but a mistyped tag is invisible later, so confirm the tag picker shows the tag under Ability -> Type rather than letting you type a new one.

2. Build the tile

Make a Widget Blueprint parented to CrimsonCoreAbilityBookEntryWidget. Name an image Image_Icon and a text block Text_Name and they bind automatically; both are optional, so a tile that is pure art still works. On Entry Set hands you the entry and its draggable flag - draw your lock or your drag affordance there. On Drag State Changed fires when a drag starts and again when it ends, however it ended.

Both ends of the drag
A drag that someone accepted never raises OnDragCancelled, and a drag that hit nothing never raises OnDrop. The tile listens to both and routes them to the same handler, so On Drag State Changed (false) is guaranteed. Do the same in any custom drag source or your tiles will stay faded after a successful drop.

Content Browser -> right-click -> User Interface -> Widget Blueprint, then pick CrimsonCoreAbilityBookEntryWidget as the parent. /CrimsonCore/UserInterface/AbilityBook/W_AbilityBook_Entry is the shipped reference; duplicate it rather than starting blank. Set Drag Visual Class if you want something richer than a translucent copy of the icon under the cursor.

3. Build the screen

Make a Widget Blueprint parented to CrimsonCoreAbilityBookScreen. It looks for three optional children by name: a UCrimsonTabListWidgetBase called TabList_Types, a UCrimsonTileView called TileView_Abilities, and a text block called Text_Empty shown while the visible tab has nothing in it.

You do not need a tab list of your own
CrimsonUI ships W_CrimsonHorizontalTabList (already parented to UCrimsonTabListWidgetBase, already carrying the TabButtonBox panel the base parents buttons into) and W_CrimsonButtonTab (already implementing ICrimsonTabButtonInterface). Drop an instance of the first in as TabList_Types, point Type Tab Button Class at the second, and the book matches every other Crimson screen. No subclass, no per-tab content widget: the screen registers its tabs with RegisterDynamicTab and re-feeds one grid when the tab changes.
The tile view needs a factory rule
UCrimsonTileView resolves the widget class per item through its FactoryRules, and an empty FactoryRules is a compile error, not a warning. Add one Crimson Widget Factory Class rule with Data Class = CrimsonCoreAbilityBookEntryObject and Widget Class = your tile, and set Entry Widget Class to the same tile as the fallback.
Class DefaultWhat to set
AbilityTypeFilterThe types to list, one tab each, in this order. Ability.Type.Active, Ability.Type.Passive.
DraggableAbilityTypesWhich of those the player may bind. Ability.Type.Active alone gives the WoW behavior. Empty means all.
TypeTabButtonClassThe tab button Widget Blueprint. Required - without it no tab gets a button.
TypeTabLabelsOptional label per type tag. A tag with no entry falls back to the tag's leaf name, so Ability.Type.Active already reads "Active" and most projects can leave this empty.
AbilityProviderIdOnly needed when the bar's layout authors more than one ability provider. Empty takes the first.
InputConfigMenu, so the book takes input while it is up.

Duplicate /CrimsonCore/UserInterface/AbilityBook/W_AbilityBook_Screen and repoint its tile class. Refresh Abilities is callable from the graph if something outside the ability system should force a rebuild.

Verify
Compile the screen Blueprint. A missing factory rule fails here with "has no Factory Rules defined" - that is the intended early catch, not a bug.

4. Open it with a key

The book is a screen, not a HUD element, so it is pushed onto a UI layer rather than registered at an extension point. CrimsonCore already ships the pattern the inventory and skill tree screens use: an Input Action, an input tag that names it, and a gameplay ability that pushes the widget onto the menu layer.

  1. Make an Input Action IA_UI_OpenAbilityBook (Boolean, one Pressed trigger) and map it to B in IMC_CrimsonCore_Interaction - the context where I opens the inventory and K the skill tree.
  2. Add the pair to InputConfig_CrimsonCore's Ability Input Actions, so the tag InputTag.OpenWidgets.AbilityBook resolves to that action.
  3. Make GA_ShowWidget_AbilityBook as a child of GA_ShowWidget_WhenInputPressed - a child inherits the graph, where a duplicate would copy it - and set its Widget Class to your screen and Layer Name to UI.Layer.Menu.
  4. Grant that ability in AbilitySet_UIMenus against the same input tag, beside the other screen-opening abilities.
Pick a free key
P - what WoW uses for the spellbook - is already IA_UI_OpenParty in this project, and K is the skill tree. B reads as "book" and is unmapped in every shipped context. Check yours before mapping: one key carries one binding, and a clash is silent.
Verify
Press the key in play. Both tabs appear, each populated; movement, camera and UI abilities are absent because you never tagged them.

5. Drag onto the bar

There is no step here, and that is the design. An action bar slot accepts any UCrimsonSlotBindingDragDropOperation: it pre-flights the drop through CanAssignBinding while you hover (so a slot that refuses the binding says so before you let go) and commits it through AssignBinding, which is already a validated client-to-server write. The book produces exactly that operation and nothing else.

The binding itself comes from the bar's own ability provider rather than one the book creates. That is deliberate: the identity a dragged tile carries is then byte-identical to the one the bar would have built for that ability by itself, so a hand-placed ability and an auto-placed one are the same entry in the store and survive a save the same way.

Verify
Drag an Active tile onto an empty slot - the slot shows accept feedback on hover and binds on release, with the same icon and name the book showed. Drop one on nothing and the tile un-fades. Try to drag a Passive tile and no drag starts, but the tile still hovers and tooltips.
Drag is mouse-only
A gamepad navigates the book and its tabs, but drag-and-drop is a pointer interaction. Gamepad players bind through the action bar's own binding picker instead; the book is a browser for them, not an editor.

See also

  • Radial Slot Bindings - the same binding vocabulary, driving a wheel instead of a grid.
  • UI Showcases - where CrimsonCore's screens are pushed from and how the HUD layout is assembled.
  • Character Pipeline - granting abilities in the first place.