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.
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.
| Type | Role |
|---|---|
UCrimsonCoreAbilityBookScreen | The 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. |
UCrimsonCoreAbilityBookEntryObject | One 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. |
UCrimsonCoreAbilityBookEntryWidget | One tile, on UCrimsonButtonBase + IUserObjectListEntry. Loads its own icon, and on drag produces a UCrimsonSlotBindingDragDropOperation - the operation an action bar slot already understands. |
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.
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.
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.
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.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 Default | What to set |
|---|---|
AbilityTypeFilter | The types to list, one tab each, in this order. Ability.Type.Active, Ability.Type.Passive. |
DraggableAbilityTypes | Which of those the player may bind. Ability.Type.Active alone gives the WoW behavior. Empty means all. |
TypeTabButtonClass | The tab button Widget Blueprint. Required - without it no tab gets a button. |
TypeTabLabels | Optional 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. |
AbilityProviderId | Only needed when the bar's layout authors more than one ability provider. Empty takes the first. |
InputConfig | Menu, 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.
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.
- Make an Input Action
IA_UI_OpenAbilityBook(Boolean, one Pressed trigger) and map it to B inIMC_CrimsonCore_Interaction- the context whereIopens the inventory andKthe skill tree. - Add the pair to
InputConfig_CrimsonCore's Ability Input Actions, so the tagInputTag.OpenWidgets.AbilityBookresolves to that action. - Make
GA_ShowWidget_AbilityBookas a child ofGA_ShowWidget_WhenInputPressed- a child inherits the graph, where a duplicate would copy it - and set its Widget Class to your screen and Layer Name toUI.Layer.Menu. - Grant that ability in
AbilitySet_UIMenusagainst the same input tag, beside the other screen-opening abilities.
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.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.
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.