Events you can listen to¶
Designers and programmers · 10 min read · Simple Blueprints
Who this is for: anyone making a Blueprint react to the game (a HUD element that updates when the party changes, a door that opens when a quest is turned in). The Sent by column and the notes marked For programmers are for C++ readers; a designer needs only the channel and payload.
When something changes in the game (the bag, the party, a quest, the locked-on target), XenoCore announces it on a gameplay message channel, using Lyra's Gameplay Message Router. A channel is a gameplay tag; a message carries a small struct. Any Blueprint can listen without knowing who sends it, which is how every XenoCore screen and HUD element stays current. The idea that makes the rest make sense: each channel has exactly one sender, and most messages only say "this changed, read it again", so a listener always re-reads the owning component rather than trusting a copy.
How it fits together¶
- Sender: one XenoCore system per channel announces the change. Each tag below is broadcast from one place only.
- Listener: a Blueprint uses the Listen for Gameplay Messages node (below).
- Channels are gameplay tags, so they appear in the tag picker.
For programmers
Senders call UGameplayMessageSubsystem::BroadcastMessage; C++ listeners use
UGameplayMessageSubsystem::RegisterListener. The channel tags are native, declared in
Source/XenoCoreRuntime/Public/XenoCoreGameplayTags.h.
The channels¶
| Channel | Payload struct and fields | Sent by | When |
|---|---|---|---|
XenoCore.Inventory.Message.Changed |
XenoCoreInventoryChangedMessage: Owner (the controller holding the bag), Item Def, New Count, Delta (negative for a removal) |
UXenoCoreInventoryStatics (also UXenoCoreInventoryWatchSubsystem, for a change made around it) |
Every add or remove made through the inventory statics: pickups, chest takes, quest turn-ins, shop and trade, save restore |
XenoCore.Equipment.Message.SlotChanged |
XenoCoreEquipmentSlotChangedMessage: Owner (the character), Slot, Item (empty when the slot was emptied) |
UXenoCoreEquipmentComponent on that character |
Something was equipped into, or removed from, a slot |
XenoCore.Party.Message.Changed |
XenoCorePartyChangedMessage: Owning Controller, Active Count |
UXenoCorePartyComponent |
A member joined, left, was benched or brought in, or the leader changed |
XenoCore.Party.Message.Wiped |
XenoCorePartyWipedMessage: Owning Controller, Phase (Defeated or Respawned), Respawn Transform |
UXenoCorePartyComponent |
Twice per party wipe: Defeated when every active member is down, before the fade (the respawn point is already decided, so a "defeated" card can name it); Respawned when the party stands again at full health and the fade back in starts. Nothing in the plugin listens; use it for a defeat card or a "returned to..." line |
XenoCore.Party.Message.TacticChanged |
XenoCoreTacticChangedMessage: Owning Controller, Member (empty for the whole party), Tactic, Value (-1 when a member went back to following the party) |
UXenoCorePartyComponent |
A party tactic changed, for the party or one member |
XenoCore.PartyGauge.Message.Changed |
XenoCorePartyGaugeChangedMessage: Old Points, New Points, Max Points, Points Per Bar, Reason (a XenoCore.PartyGauge.Reason.* tag; empty for the out-of-battle drain) |
UXenoCorePartyGaugeComponent |
The Party Gauge moved. Unlike most channels it carries the numbers, so a HUD can animate from old to new |
XenoCore.PartyGauge.Message.Refused |
XenoCorePartyGaugeChangedMessage (old and new points are equal) |
UXenoCorePartyGaugeComponent |
The player asked for bars the gauge does not have, such as a revive with less than one bar. The gauge widget shakes its short bars |
XenoCore.Progression.Message.Changed |
XenoCoreProgressionChangedMessage: Owning Controller, Levelled Count, Experience Awarded, Gains (a list of Member, Experience, Art Points, Skill Points) |
UXenoCoreProgressionComponent |
Experience, Art Points or Skill Points were banked, anyone levelled, gold changed, or skill progress changed. A kill sends one message for its EXP, AP and SP together |
XenoCore.Progression.Message.LevelUp |
XenoCoreLevelUpMessage: Owning Controller, Levelled (a list of Member, Level Before, Level After) |
UXenoCoreProgressionComponent |
Once per award that levelled at least one character. One message for the whole party |
XenoCore.Skills.Message.SkillLearnt |
XenoCoreSkillLearntMessage: Member, Branch (row name), Index (0 to 4), Skill Name |
UXenoCoreProgressionComponent |
A character learnt a skill, once per skill. Nothing in the plugin listens to it; the "Skill Learnt" float is drawn directly. See Skills |
XenoCore.Enemy.Message.Defeated |
XenoCoreEnemyDefeatedMessage: Enemy Class, Display Name, Level |
AXenoCoreEnemyCharacter |
An enemy's death starts. Display Name is empty if the enemy has none authored |
XenoCore.Battle.Message.Won |
XenoCoreBattleWonMessage: Celebrants (every living party body told to sheathe and celebrate) |
UXenoCoreBattleVictorySubsystem |
A battle was won: an enemy died and no living enemy is still fighting the party. Never sent for a retreat. The fanfare, the disarm and the victory emotes follow; area music fades its battle track on it |
XenoCore.Quest.Message.Changed |
XenoCoreQuestChangedMessage: Owning Controller |
UXenoCoreQuestComponent |
Any quest was accepted, declined, progressed, turned in, restored, or the tracked quest changed |
XenoCore.Quest.Message.TurnedIn |
XenoCoreQuestTurnedInMessage: Quest, Quest Title, Gold, Experience, Items |
UXenoCoreQuestComponent |
A quest was handed in. Carries what it paid |
XenoCore.Targeting.Message.TargetChanged |
XenoCoreTargetChangedMessage: Seeker, New Target, Old Target |
The target mode ability (UXenoCoreGameplayAbility_TargetMode) |
The locked-on target changed. New Target is empty when target mode ends |
XenoCore.Battle.Message.Notification |
XenoCoreBattleNotification: Target, Text, Colour, Scale, Punch |
UXenoCoreBattleNotifySubsystem (anyone may call its Notify) |
A word pops over a character: the damage calculation raises MISS, CRITICAL HIT! and BLOCKED |
XenoCore.Loot.Message.ChestOpened |
XenoCoreChestOpenedMessage: Chest, Tier, Offered (item definitions) |
AXenoCoreTreasureChest |
A chest was opened and its contents rolled, before anything enters the bag |
XenoCore.Collectable.Message.Collected |
XenoCoreCollectableCollectedMessage: Item Def, Instance, Orb |
AXenoCoreCollectableOrb |
A collectable orb was picked up |
XenoCore.Voice.Message.Spoke |
XenoCoreVoiceSpokeMessage: Speaker, Category, Take (sound and lip-sync keys), Expression, Expression Weight, Subtitle, Duration |
UXenoCoreBattleVoiceSubsystem |
A party member's voice line starts. See Battle voices |
XenoCore.Time.Message.PhaseChanged, .HourBlockChanged, .Jumped |
XenoCoreTimeChangedMessage: Minutes, Old Phase, New Phase, Hour Block, Jumped |
UXenoCoreTimeOfDaySubsystem |
The phase changed (Morning, Day, Evening, Night); a new three-hour block began; the clock was jumped (Change Time, a load). A jump across several phases sends one message with the final phase. See Time of day and weather |
XenoCore.Weather.Message.Changed |
XenoCoreWeatherChangedMessage: Old Weather, New Weather, Instant |
UXenoCoreWeatherSubsystem |
The weather changed. Instant is true when it snapped rather than blended |
The Owner of an inventory message is the player controller, because the party bag lives there. The Owner of an equipment message is the character wearing the gear.
Listening from Blueprint¶
- In any Blueprint with a world (a widget, an actor, a component), add Listen for Gameplay Messages. Do it once, for example on Event Construct or Begin Play.
- Set Channel to the tag, for example
XenoCore.Quest.Message.Changed. - Set Payload Type to the matching struct from the table. It must match exactly.
- From On Message Received, use the Payload pin, split or break it, and react.
- For a "changed" message, re-read the owning component (for example Get Party Quests then Get Status) rather than reading details from the message.
Match Type can be left at Exact Match. Partial Match would also receive child tags, which no XenoCore channel has today.
The wrong Payload Type drops the message
If Payload Type does not match the struct the sender uses, your node never fires. The only
sign is a Struct type mismatch on channel error in the Output Log, easy to miss during play.
Copy the struct name from the table.
Fails silently: listening again every time a screen opens
CommonUI reuses screens. If you start listening in On Activated, each visit adds another listener and the handler runs several times. Listen once, from Construct.
The rule for the bag¶
Every change to the party bag goes through the XenoCore inventory nodes
Add and remove items with the XenoCore › Inventory nodes (Add Item to Party, Remove
Item from Party, Consume Items From Party and the rest), never with the Lyra inventory
component's own functions such as Add Item Definition. The statics broadcast
XenoCore.Inventory.Message.Changed at once, honour carry caps and never take a worn item.
A change made any other way is caught by a watcher that broadcasts it a frame later, but it
gets none of those protections.
Two consequences worth knowing:
- Lyra's own inventory message does not fire here. Lyra's
Lyra.Inventory.Message.StackChangedis only sent from replication callbacks, which a single-player game never runs. That is why XenoCore has its own channel. - Item costs go through the statics. The Party Item (XenoCore) ability cost spends items with Consume Items From Party, so an item spent as a cost sends the message too. (Lyra's own Inventory Item cost is switched off in stock Lyra and never lets an ability activate.)
The same one-sender rule holds for gold (through the progression component), the party (the party component) and quests (the quest component).
Extending it¶
- A new channel for your own system: add a tag in Project Settings › Project › GameplayTags
(for example
MyGame.Door.Message.Opened), make a Blueprint Structure for the payload, and send it with Lyra's Broadcast Message node from exactly one place. Listeners use Listen for Gameplay Messages with that structure. Prefer "this changed, re-read me" messages; add details only when a listener cannot reconstruct them, as the quest turn-in payout does. (In C++: a native tag and aUSTRUCT(BlueprintType)withBlueprintReadOnlyfields.) - Raising a battle notification from a Blueprint: call the Notify node (XenoCore › Battle) with a target actor, text and colour. The message and the popup follow.
- The full list of tags is on Gameplay tags, and the payload structs with every field on Gameplay messages.
Why it's built this way¶
For programmers
- Lyra's router, not delegates. A message needs no reference between sender and listener, so a HUD widget can react to the party component without ever finding it, and a new screen needs no change to the system it watches. It is the same router Lyra uses for its own events.
- One sender per channel. If two systems broadcast the same channel, a listener cannot know which view of the world is current. Keeping one broadcaster per channel is what makes "re-read on message" safe.
- Thin messages. A message carrying a snapshot can be stale by the time it is read. A message saying "read again" cannot. The two exceptions (turn-in payout, level-up list) carry facts that cannot be re-read afterwards.
Next: Reference: gameplay messages · Quests