Skip to content

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

  1. 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.
  2. Set Channel to the tag, for example XenoCore.Quest.Message.Changed.
  3. Set Payload Type to the matching struct from the table. It must match exactly.
  4. From On Message Received, use the Payload pin, split or break it, and react.
  5. 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.StackChanged is 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 a USTRUCT(BlueprintType) with BlueprintReadOnly fields.)
  • 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