Gameplay messages¶
Programmer · reference · No code
XenoCore announces changes through Lyra's Gameplay Message Router: a sender broadcasts a payload struct on a gameplay-tag channel, and any number of listeners receive it without knowing who sent it. This is how the HUD, menus and quests stay up to date. For the idea behind it, see Events and messages.
To listen from a Blueprint, use Lyra's Listen for Gameplay Messages latent node, set Channel
to the tag and Payload Type to the struct below. From C++, use
UGameplayMessageSubsystem::Get(this).RegisterListener(...) and keep the returned handle so you can
unregister in EndPlay or NativeDestruct. Every payload struct is BlueprintType.
Channels¶
| Channel | Payload | Sent by | Sent when |
|---|---|---|---|
XenoCore.Inventory.Message.Changed |
FXenoCoreInventoryChangedMessage |
UXenoCoreInventoryStatics (every add, remove and consume it performs); UXenoCoreInventoryWatchSubsystem for a change made with Lyra's own inventory functions, one frame later |
The party bag gains or loses items |
XenoCore.Equipment.Message.SlotChanged |
FXenoCoreEquipmentSlotChangedMessage |
UXenoCoreEquipmentComponent |
A character's slot is equipped, swapped or emptied |
XenoCore.Party.Message.Changed |
FXenoCorePartyChangedMessage |
UXenoCorePartyComponent only |
The roster, the active party or the leader changes |
XenoCore.Party.Message.TacticChanged |
FXenoCoreTacticChangedMessage |
UXenoCorePartyComponent only |
A party tactic changes, for the whole party or one member |
XenoCore.Party.Message.Wiped |
FXenoCorePartyWipedMessage |
UXenoCorePartyComponent only |
Twice per party wipe: when every active member is down, and when the party has respawned |
XenoCore.PartyGauge.Message.Changed |
FXenoCorePartyGaugeChangedMessage |
UXenoCorePartyGaugeComponent only |
The Party Gauge gains or loses points, with the reason |
XenoCore.PartyGauge.Message.Refused |
FXenoCorePartyGaugeChangedMessage |
UXenoCorePartyGaugeComponent (Notify Cannot Afford) |
The player asked for bars the gauge does not hold, such as a revive with the gauge short. Nothing changed |
XenoCore.Progression.Message.Changed |
FXenoCoreProgressionChangedMessage |
UXenoCoreProgressionComponent only |
EXP is banked, a level is gained, or gold changes |
XenoCore.Progression.Message.LevelUp |
FXenoCoreLevelUpMessage |
UXenoCoreProgressionComponent |
Once per award that raised at least one level, after the Changed message |
XenoCore.Skills.Message.SkillLearnt |
FXenoCoreSkillLearntMessage |
UXenoCoreProgressionComponent |
A character learns a skill |
XenoCore.Quest.Message.Changed |
FXenoCoreQuestChangedMessage |
UXenoCoreQuestComponent only |
Any quest is offered, accepted, declined, progressed or turned in |
XenoCore.Quest.Message.TurnedIn |
FXenoCoreQuestTurnedInMessage |
UXenoCoreQuestComponent |
Once, when a quest is handed in, carrying the payout |
XenoCore.Enemy.Message.Defeated |
FXenoCoreEnemyDefeatedMessage |
AXenoCoreEnemyCharacter |
An enemy's death starts (the same moment EXP and its chest are awarded) |
XenoCore.Loot.Message.ChestOpened |
FXenoCoreChestOpenedMessage |
AXenoCoreTreasureChest |
A chest is opened and its contents rolled |
XenoCore.Collectable.Message.Collected |
FXenoCoreCollectableCollectedMessage |
AXenoCoreCollectableOrb |
A collectable orb is picked up and its item banked |
XenoCore.Targeting.Message.TargetChanged |
FXenoCoreTargetChangedMessage |
UXenoCoreGameplayAbility_TargetMode |
The locked target changes, including to none when the lock ends |
XenoCore.Battle.Message.Notification |
FXenoCoreBattleNotification |
UXenoCoreBattleNotifySubsystem (the Notify node) |
Battle text should appear over an actor: miss, break, a buff name |
XenoCore.Battle.Message.Won |
FXenoCoreBattleWonMessage |
UXenoCoreBattleVictorySubsystem only |
A battle is won (the last fighting enemy fell with the party in battle), as the fanfare starts and the party sheathes |
XenoCore.Voice.Message.Spoke |
FXenoCoreVoiceSpokeMessage |
UXenoCoreBattleVoiceSubsystem |
A party member starts a voice line |
XenoCore.Time.Message.PhaseChanged |
FXenoCoreTimeChangedMessage |
UXenoCoreTimeOfDaySubsystem |
The clock enters Morning, Day, Evening or Night |
XenoCore.Time.Message.HourBlockChanged |
FXenoCoreTimeChangedMessage |
UXenoCoreTimeOfDaySubsystem |
The clock enters a new three-hour block |
XenoCore.Time.Message.Jumped |
FXenoCoreTimeChangedMessage |
UXenoCoreTimeOfDaySubsystem |
The time was set (Change Time, a load, a console command) rather than reached |
XenoCore.Weather.Message.Changed |
FXenoCoreWeatherChangedMessage |
UXenoCoreWeatherSubsystem |
The weather changes |
Sources: the BroadcastMessage calls in these files under Source/XenoCoreRuntime/Private/
(search the file for BroadcastMessage): XenoCoreInventoryStatics.cpp,
XenoCoreInventoryWatchSubsystem.cpp, XenoCoreEquipmentComponent.cpp,
XenoCorePartyComponent.cpp, XenoCorePartyGaugeComponent.cpp,
XenoCoreProgressionComponent.cpp, XenoCoreProgressionComponent_Skills.cpp,
XenoCoreQuestComponent.cpp, XenoCoreEnemyCharacter.cpp, XenoCoreTreasureChest.cpp,
XenoCoreCollectableOrb.cpp, XenoCoreTargetModeAbility.cpp, XenoCoreBattleNotifySubsystem.cpp,
XenoCoreBattleVictorySubsystem.cpp, XenoCoreBattleVoiceSubsystem.cpp,
XenoCoreTimeOfDaySubsystem.cpp and XenoCoreWeatherSubsystem.cpp.
Payloads¶
FXenoCoreInventoryChangedMessage¶
| Field | Type | Meaning |
|---|---|---|
Owner |
AActor* |
The bag's owner: the player controller |
ItemDef |
TSubclassOf<ULyraInventoryItemDefinition> |
The item that changed |
NewCount |
int32 |
How many the party holds after the change |
Delta |
int32 |
Positive when gained, negative when spent |
FXenoCoreEquipmentSlotChangedMessage¶
| Field | Type | Meaning |
|---|---|---|
Owner |
AActor* |
The pawn whose loadout changed |
Slot |
EXenoCoreEquipSlot |
Which slot |
Item |
ULyraInventoryItemInstance* |
What is in the slot now; null when it was emptied |
FXenoCorePartyChangedMessage¶
| Field | Type | Meaning |
|---|---|---|
OwningController |
AController* |
The controller whose party changed |
ActiveCount |
int32 |
How many characters are active now |
FXenoCoreTacticChangedMessage¶
| Field | Type | Meaning |
|---|---|---|
OwningController |
AController* |
The controller whose party it is |
Member |
UXenoCorePartyMemberDefinition* |
The member whose own order changed; null for a whole-party change |
Tactic |
EXenoCoreTactic |
Which tactic |
Value |
int32 |
The new value. For a member, INDEX_NONE means the order was handed back to the party's |
FXenoCorePartyWipedMessage¶
| Field | Type | Meaning |
|---|---|---|
OwningController |
AController* |
The controller whose party was wiped |
Phase |
EXenoCorePartyWipePhase |
Defeated: every active member is down and the fade to black is about to start. Respawned: the party stands again at full health and the fade back in has started |
RespawnTransform |
FTransform |
Where the party is sent. Already final at Defeated, so a UI can name the place before the fade |
FXenoCorePartyGaugeChangedMessage¶
Used by both Party Gauge channels. On Refused, the old and new points are equal.
| Field | Type | Meaning |
|---|---|---|
OldPoints |
float |
Points before the change |
NewPoints |
float |
Points after it |
MaxPoints |
float |
The whole gauge (300 by default) |
PointsPerBar |
float |
One bar (100 by default) |
Reason |
FGameplayTag |
Why: a XenoCore.PartyGauge.Reason.* tag. Empty for the out-of-battle drain |
FXenoCoreProgressionChangedMessage¶
| Field | Type | Meaning |
|---|---|---|
OwningController |
AController* |
The controller whose party progressed |
LevelledCount |
int32 |
How many characters levelled in this award |
ExperienceAwarded |
int32 |
The EXP this award was worth |
FXenoCoreLevelUpMessage¶
| Field | Type | Meaning |
|---|---|---|
OwningController |
AController* |
The controller whose party levelled |
Levelled |
TArray<FXenoCoreLevelUpEntry> |
One entry per character that levelled: Member, LevelBefore, LevelAfter |
FXenoCoreSkillLearntMessage¶
| Field | Type | Meaning |
|---|---|---|
Member |
UXenoCorePartyMemberDefinition* |
Who learnt it |
Branch |
FName |
The skill branch |
Index |
int32 |
Position in the branch, 0 to 4, top to bottom |
SkillName |
FText |
The skill's display name |
FXenoCoreQuestChangedMessage¶
| Field | Type | Meaning |
|---|---|---|
OwningController |
AController* |
The controller whose quests moved |
This message says only that something changed. Listeners re-read the quest component for the details.
FXenoCoreQuestTurnedInMessage¶
| Field | Type | Meaning |
|---|---|---|
Quest |
UXenoCoreQuestDefinition* |
The quest handed in |
QuestTitle |
FText |
Its title, copied at the time |
Gold |
int32 |
Gold paid |
Experience |
int32 |
EXP paid |
Items |
TArray<TSubclassOf<ULyraInventoryItemDefinition>> |
Items handed over |
This one carries the payout because it cannot be re-read afterwards: a moment later the gold is part of a running total and the items may already be spent.
FXenoCoreEnemyDefeatedMessage¶
| Field | Type | Meaning |
|---|---|---|
EnemyClass |
TSubclassOf<AActor> |
What died. Kill objectives match it with is child of, so an objective can name a base class. |
DisplayName |
FText |
The enemy's name as shown on its target badge. Empty if the enemy has none authored |
Level |
int32 |
The enemy's level |
FXenoCoreChestOpenedMessage¶
| Field | Type | Meaning |
|---|---|---|
Chest |
AActor* |
The chest |
Tier |
EXenoCoreChestTier |
Wooden, Silver or Gold |
Offered |
TArray<TSubclassOf<ULyraInventoryItemDefinition>> |
What the chest offers. Nothing is in the bag yet: the loot panel lets the player take or leave it. |
FXenoCoreCollectableCollectedMessage¶
| Field | Type | Meaning |
|---|---|---|
ItemDef |
TSubclassOf<ULyraInventoryItemDefinition> |
What was picked up. Never null: a refused pickup sends nothing. |
Instance |
ULyraInventoryItemInstance* |
The instance that entered the bag |
Orb |
AActor* |
The orb |
FXenoCoreTargetChangedMessage¶
| Field | Type | Meaning |
|---|---|---|
Seeker |
AActor* |
The pawn doing the targeting |
NewTarget |
AActor* |
What is now selected; null when the lock ends |
OldTarget |
AActor* |
What was selected before |
FXenoCoreBattleNotification¶
| Field | Type | Meaning |
|---|---|---|
Target |
AActor* |
Whose head the text appears over |
Text |
FText |
The text |
Colour |
FLinearColor |
Its colour |
Scale |
float |
Draw size |
Send your own with the Notify node (XenoCore › Battle). It broadcasts on this channel, so anything else listening (a combat log, a tutorial) sees your text too.
FXenoCoreBattleWonMessage¶
| Field | Type | Meaning |
|---|---|---|
Celebrants |
TArray<ACharacter*> |
Every living party body told to sheathe and celebrate |
FXenoCoreVoiceSpokeMessage¶
| Field | Type | Meaning |
|---|---|---|
Speaker |
AActor* |
Who is speaking |
Category |
FGameplayTag |
The line's XenoCore.Voice.* category |
Take |
FXenoCoreVoiceTake |
The take being played |
Expression, ExpressionWeight |
FName, float |
The face the line asks for |
Subtitle |
FText |
The subtitle text |
Duration |
float |
How long the line lasts, in seconds |
FXenoCoreTimeChangedMessage¶
Used by all three time channels.
| Field | Type | Meaning |
|---|---|---|
Minutes |
float |
Minutes since midnight now |
OldPhase, NewPhase |
EXenoCoreTimePhase |
Morning, Day, Evening or Night, before and after |
HourBlock |
int32 |
The three-hour block now |
bJumped |
bool |
The time was set rather than reached; lighting snaps instead of blending |
FXenoCoreWeatherChangedMessage¶
| Field | Type | Meaning |
|---|---|---|
OldWeather, NewWeather |
FGameplayTag |
The weather before and after |
bInstant |
bool |
The change snaps rather than blends |
Who listens¶
| Channel | Listeners in C++ |
|---|---|
Inventory.Message.Changed |
UXenoCoreQuestComponent (re-counts collection quests); also a console self-test on UXenoCoreEquipmentComponent |
Equipment.Message.SlotChanged |
UXenoCoreStatBlockBase (stat panels) |
Party.Message.Changed |
UXenoCorePartyMemberPlateBase (HUD plates) |
Party.Message.Wiped |
None. Yours to use, for a defeat card or a "returned to..." line |
PartyGauge.Message.Changed |
UXenoCorePartyGaugeWidget (the gauge readout) |
PartyGauge.Message.Refused |
UXenoCorePartyGaugeWidget (shakes and flashes red) |
Progression.Message.Changed |
UXenoCorePlayerPlateBase (HUD plates) |
Progression.Message.LevelUp |
UXenoCoreLevelUpBannerBase |
Quest.Message.Changed |
UXenoCoreQuestLogBase, UXenoCoreQuestTrackerBase |
Quest.Message.TurnedIn |
UXenoCoreQuestRewardBase |
Enemy.Message.Defeated |
UXenoCoreQuestComponent (kill objectives) |
Loot.Message.ChestOpened |
UXenoCoreLootPopupSubsystem (opens the loot panel) |
Collectable.Message.Collected |
UXenoCoreItemBadgeBase |
Battle.Message.Notification |
UXenoCoreBattleNotifySubsystem (draws the text) |
Battle.Message.Won |
AXenoCoreAreaMusic (fades the battle track), UXenoCoreBattleVoiceSubsystem (victory lines) |
Weather.Message.Changed |
AXenoCoreEnvironmentDirector (lights, sky and rain) |
Party.Message.TacticChanged, Voice.Message.Spoke, Time.Message.* |
None in C++. Yours to use |
No shipped Blueprint listens to a XenoCore channel; all listeners are the C++ bases above.
XenoCore also listens to one Lyra channel: UXenoCoreDamageNumberSubsystem draws taken-damage and
heal numbers from Lyra's damage message.
Rules and traps¶
Fails silently: Lyra's own inventory message never fires here
Lyra's Lyra.Inventory.Message.StackChanged is only sent from network-replication callbacks.
XenoCore is single-player, so it never arrives. Listen to
XenoCore.Inventory.Message.Changed instead.
Changing the bag behind XenoCore's back
UXenoCoreInventoryStatics (the XenoCore › Inventory nodes) sends the inventory message
directly. A change made through Lyra's inventory manager is noticed by
UXenoCoreInventoryWatchSubsystem, which sends the same message for it one frame later and logs
it to LogXenoBagWatch. So listeners still hear it, but a frame late, and with none of the
statics' protections (carry caps, never removing a worn item). The message's Owner is then the
controller that owns the bag.
Screens should re-read, not accumulate
Several messages carry only "something changed". Treat every message as a prompt to re-read the owning component, never as a delta to add up. A running total drifts the first time a change arrives without a message.
- Add your own channel rather than reusing one: each XenoCore channel has one sender by design, so listeners can trust where it came from. Declare the tag in your own tag file and your own payload struct.
- Unregister in
EndPlayorNativeDestruct. A widget that is pooled and re-shown should register once, not on every show.