Four characters stand in a room, and what each of them says depends on the day and the time of day. Several pieces of content can often run at once, and Yarn Spinner picks one. Alice, Barry and Liz each have a node group, and George has a line group.
When you run it, the sign at the back of the room says “It is Monday morning.” A switch on the floor in front of the sign moves time on. Walk into it and the sign changes to Monday evening, then Tuesday morning, and so on. Talk to the characters before and after, and they say different things.

Running it
Open samples/basic-saliency/basic-saliency.tscn and press Run Current Scene.
| Key | What it does |
|---|---|
| W, A, S, D or the arrow keys | Move |
| E | Talk to the character you’re next to |
| Enter, Space or a left click | Go to the next line |
Walk into the orange switch at the back of the room to move time on. Each time, the time goes from morning to evening, or from evening to the next morning. After Wednesday evening, it goes back to Monday morning.
The blue character in the front right is Capsley, who explains the sample. Capsley’s node has when: once, so after you’ve talked to Capsley once, the speaking indicator doesn’t appear over Capsley again.
How it works
The day and the time are two Yarn variables, $day and $time. Every node in the characters’ node groups has when: headers that check them. Each time you talk to a character, the Dialogue Runner works out which of that character’s nodes can run, and its saliency strategy picks one. The sample leaves the Dialogue Runner’s Saliency Strategy at the default, Random Best Least Recent. See Saliency and Storylets for how the strategies pick.
The day and the time
The Yarn Script declares two enums, and a variable for each:
<<enum Day>>
<<case Monday>>
<<case Tuesday>>
<<case Wednesday>>
<<endenum>>
<<enum TimeOfDay>>
<<case Morning>>
<<case Evening>>
<<endenum>>
/// the current day
<<declare $day = Day.Monday>>
/// the current time
<<declare $time = TimeOfDay.Morning>>Nothing runs the Setup node. Declarations apply to the whole Yarn Project, so it never has to to run.
In a condition, .Monday is short for Day.Monday. Yarn Spinner works out the enum from the variable it’s compared with.
Node groups
Alice has a node group with eight nodes. Some depend on the day, and some on the day and the time. On Wednesday evening, two of them can run:
title: Alice
when: $day == .Wednesday
when: $time == .Evening
when: once
---
Player: Hello
Alice: the week is half over, what a relief.
===
title: Alice
when: $day == .Wednesday
when: $time == .Evening
---
Player: Hello
Alice: Probably should go to bed, it is Wednesday evening after all.
===once, so this node’s complexity is 3. It can only run once.once, so this node’s complexity is 2.The default strategy first looks for the candidates seen the fewest times, then picks the one with the highest complexity. The first time you talk to Alice on a Wednesday evening, neither node has been seen, so the once node wins. After that it stops passing, and Alice says the other line.
The other characters show other ways of writing node groups:
- Barry has three nodes for the morning and three for the evening, each with one condition. The default strategy picks the one seen the fewest times, so Barry works through all three before he repeats one.
- Liz’s nodes don’t depend on the day or the time. Three of them have
when: always, and one haswhen: onceandwhen: always. Theoncenode has the highest complexity, so it’s the first thing Liz says. - Alice has two nodes for Tuesday evening and two for Wednesday morning, with the same conditions. When nodes tie like this, she takes turns between them.
Each character’s DialogueInteractable starts the node group with the character’s name, such as Alice. Before showing the speaking indicator, it checks that something in the group can run, with has_salient_content(). See Looking at a node group from GDScript.
A line group
George’s node is a single node with a line group in it. Each line starts with =>, and some have a condition:
title: George
---
=> George: oh hi
=> George: another Monday, I hate Mondays <<if $day == .Monday>>
=> George: I suppose I should make breakfast <<if $time == .Morning>>
=> George: Weird how tomorrow is Monday, right? <<if $day == .Wednesday && $time == .Evening>>
Player: did you want the devs to have to write out even more sample content?
George: it would be nice, yes
Player: well they aren't gonna do that
George: boo
=> George: I wonder if I am in any other samples
===The Dialogue Runner picks one line from the group with the same strategy as the node groups. The lines with no condition can always be picked. The Wednesday evening line has two conditions joined with &&, so it has the highest complexity, and the first time you talk to George on a Wednesday evening, he says it. The indented lines under it only run when that line is picked.
Changing the time from GDScript
The Dialogue Runner’s Variable Storage is a BasicSaliencyVariableStorage, a script that extends YarnInMemoryVariableStorage. It adds functions for reading and changing the day and the time, so that game code doesn’t need to know the variable names:
enum Day { MONDAY = 0, TUESDAY = 1, WEDNESDAY = 2 }
enum TimeOfDay { MORNING = 0, EVENING = 1 }
## the current day
func get_day() -> Day:
return int(get_float("$day", 0.0)) as Day
func set_day(value: Day) -> void:
set_value("$day", float(value))$day as a number and turn it into a Day.Day in $day as a number.The switch is an Area3D called TimeTrigger, with time_advancer.gd on it. Its body_entered signal is connected to _on_body_entered() in the scene. When the player walks into it, it moves time on through the Variable Storage, and updates the sign:
func _on_body_entered(body: Node3D) -> void:
if body.is_in_group(&"player"):
_advance_time()
_update_label()
func _advance_time() -> void:
if variable_store == null:
push_warning("TimeAdvancer: variable_store is not set")
return
# Morning steps to evening; evening wraps to the next day's morning,
# with Wednesday looping back to Monday.
if variable_store.get_time() == TimeOfDay.MORNING:
variable_store.set_time(TimeOfDay.EVENING)
return
variable_store.set_time(TimeOfDay.MORNING)
match variable_store.get_day():
Day.MONDAY:
variable_store.set_day(Day.TUESDAY)
Day.TUESDAY:
variable_store.set_day(Day.WEDNESDAY)
Day.WEDNESDAY:
variable_store.set_day(Day.MONDAY)The next time you talk to someone, the when: headers are checked against the new values. For more on custom Variable Storage, see Variables and Storage.
Things to try
- Select YarnDialogueRunner, and under Advanced, set Saliency Strategy to Best. Run the scene and talk to Barry a few times. The Best strategy doesn’t count how many times each node has been seen, so Barry says the same line every time.
- Add another node to Barry’s node group with two conditions, such as
when: $time == .Morningandwhen: $day == .Tuesday. It has a higher complexity than his other morning nodes, so it’s the first thing he says on a Tuesday morning. - Add a line to George’s line group with
<<if $time == .Evening>>at the end, and talk to him in the evening.