Skip to content

Choices, and why one is locked

Taking a choice, and drawing an option the player can see but cannot take — with the reason the engine already wrote.

All documentation — Playing a story
On this page

When a step is awaiting a choice, Get Choices holds one resolved choice per exit the player can see. Some of them may not be takeable, and drawing a locked option well is most of what makes a requirement feel like part of the game rather than a bug.

LockedChoices.cpp
#include "TaleShardChoiceLibrary.h"
#include "TaleShardStepLibrary.h"
#include "TaleShardStepTypes.h"
namespace TaleShardDocSamples
{
// Describes each offered choice, and why a locked one is locked.
TArray<FString> DescribeChoices(const FTaleShardPlayStep& Step)
{
TArray<FString> Lines;
for (const FTaleShardResolvedChoice& Choice : UTaleShardStepLibrary::GetChoices(Step))
{
// One rule for takeable, already applied: enabled, and pointing somewhere.
FString Line = Choice.bIsTakeable ? TEXT("[ ] ") : TEXT("[x] ");
// The destination lends a choice its text. The rule is already applied upstream, so
// read the content rather than deriving which fields supply it.
Line += UTaleShardChoiceLibrary::GetPlainText(Choice).ToString();
if (Choice.bVisited)
{
Line += TEXT(" (seen)");
}
Lines.Add(Line);
// A list, because a choice can be refused by its own gate AND by requirement rows
// on the node it leads to. Empty means takeable. Each entry is already a sentence:
// the author's own when they wrote one, otherwise what the player needs beside what
// they hold.
for (const FText& Reason : UTaleShardChoiceLibrary::GetBlockedBy(Choice))
{
Lines.Add(TEXT(" ") + Reason.ToString());
}
}
return Lines;
}
}

Taking one

Three verbs, and the difference is what they key on:

VerbKeys onWhen to use it
Choosethe choice struct itselfthe button holds the choice it was built from. Survives a re-draw
Choose By Idthe choice’s Idthe button holds a string
Choose Atthe index into Get Choicespositional, so it goes stale: a re-draw rebuilds the list, and a gate whose value changed can drop an option out of it. Prefer Choose anywhere the index is not read in the same frame it was produced

A choice that is drawn but cannot be taken is refused, with a reason, and nothing changes. A Choose while the story is not awaiting a choice at all is ignored silently — that is a mistimed press, not an error.

Identity is Choice Id, not Port Id. A handle legitimately carries several edges — two replies out of one outgoing handle are two edges reporting the same port — so keying your buttons by port id collapses them, and Choose By Id refuses a port id by name.

Is Takeable is the one rule, and it is already applied: enabled, and pointing somewhere. Do not compose your own from the parts.

What a choice carries

PinWhat it holds
Choice Idthis choice’s own identity, unique within the step
Port Idwhich handle this exit leaves by. Not unique within a step
KindExit for a way out of the node on screen, Board Entry when the path ended and this is one of the board’s remaining ways in
Is Takeablecan the player actually take this. Grey the option out when false
Enabledevery gate and requirement passed, or there were none
Visitedthe player has taken this exit before
Has Target, Target Node Id, Target Board Idwhere it leads. Has Target is false for a dangling exit: a port with no edge, or an edge to a missing node
Has Picture Key, Picture Keythe key of the choice’s face field on the step — a media or entity reference to draw the option as a tile with. Has Picture Key is false for a choice with no picture, which is every choice on a node whose options are not repeater rows carrying a picture field

Its label is its content — the resolved fields its destination lends it — read through the TaleShard Choice library’s Get Content and Get Plain Text. That is why the plugin’s dialogue screen reads the content rather than deriving which fields supply the text.

Drawing a choice as a tile

When a node’s options are the rows of a repeater and the row’s block carries a picture — a media field, or an entity whose portrait is the picture — each choice names that field as its face. Get Picture Key (or the Picture Key pin) gives the field’s key; pass it to the step’s Find Field By Key, and the resolved field carries the reference. Resolving the reference to a texture is yours, through the same media and entity reads every other field uses — see Media and Entity display. The key is in the same key space as the choice’s content, so a field found this way and a field from Get Content are the same kind of handle.

Why an option is locked

ReadWhat it gives
Get Blocked Byevery reason this choice is drawn but not takeable, one sentence each, in the engine’s order: the exit’s own gate first, then the destination’s requirements. Empty for a choice the player can take
Get Block Reasonwhy the player cannot take this, as one text for a tooltip: the author’s own sentence when they wrote one, otherwise what the player needs and what they hold, otherwise the requirement’s category

It is a list because a choice can be refused twice at once: by its own gate, and by a requirement row on its destination. The sentences are already localised through the story’s own language and already carry the progress reading — 6/8 rather than a bare “locked” — so a Blueprint draws them rather than assembling them.

Where a requirement actually lives

The author writes a requirement once, on the destination — “you need STRENGTH 8 to get in here” — and the runtime surfaces it on every choice that leads there. That is why a check survives an edge being deleted and redrawn, and why it travels with copy and paste.

Two consequences worth knowing:

  • A row whose policy is hide never reaches you. The option is not in Get Choices at all.
  • An authored jump target bypasses requirements. An override of the graph overrides this too.

Next

One handler per node type — where a node type’s own logic goes.

Written for
Unity developers
Last updated