Skip to content

Dialogue

Tom_XV edited this page Sep 23, 2026 · 6 revisions

English | 日本語

The Dialogue library is DragNWash.ModFramework.Dialogue.dll, in the namespace DragNWash.ModFramework.Dialogue. It tells your mod which line of dialogue or option is about to be shown, with its Yarn line ID, speaker and node. It also helps a mod find its data for a line again after a game update has edited that line.

[BepInDependency(GameDialogue.Guid, BepInDependency.DependencyFlags.HardDependency)]

Events

GameDialogue.NodeStarted += node => Logger.LogInfo($"Node {node}");

GameDialogue.LineShowing += line =>
    Logger.LogInfo($"{line.LineId} {line.Speaker}: {line.Text}");

GameDialogue.OptionShowing += option =>
    Logger.LogInfo($"Option {option.LineId} available={option.IsAvailable}: {option.Text}");

Handlers run just before the text appears. If one throws, the exception is logged and the others still run.

DialogueLine

Property Meaning
string LineId Yarn line ID, for example line:6046bedf. It stays the same across languages and across game updates that only fix typos
string Speaker The character's name as written in the script (Ryan), or null. The game's script doesn't name a speaker in most lines, so this is empty for nearly every line; see SpeakerGuess below
string Text The text without the name
string FullText The text with the name, as the script has it (Ryan: Hello)
IReadOnlyList<string> Metadata Yarn tags, for example lastline
string Node The Yarn node, when known
TMP_Text Component The component the text is about to go into
bool IsOption True for an option the player can pick
bool IsAvailable For options: false when it's struck through

Who says a line: SpeakerGuess

Experimental. This came in with framework 1.4.0 and may change or go away in a later version.

Dialogue 1.2.0 adds two properties to DialogueLine:

Property Meaning
string SpeakerGuess Who says the line: Speaker when the script names one, else a guess from the node's name (the part before the first _: Ryan_1_intro is Ryan), and Kobold (the player) for an option. Null when there's nothing to go on
string SpeakerFrom Where the guess came from: script, node, option, or null

Changing a specific line

Use Text to change the text, and TryGetLine to find out which line the rewriter is looking at:

GameText.AddRewriter(MyMod.Guid, context =>
{
    if (GameDialogue.TryGetLine(context.Component, context.Source, out DialogueLine line)
        && line.LineId == "line:6046bedf")
    {
        context.Text = "Hello there!";
    }
});

TryGetLine only matches while the component is still showing that line (with or without the name, or struck through), so whatever the component shows later won't match.

Stable line keys

Experimental. This came in with the Dialogue library 1.1.0 (released 2026-09-17).

If your mod keeps data about a line of dialogue (a translation, a bookmark, a chapter marker), it needs to find that line again after a game update. Keying by the exact English text breaks on the smallest edit. The September 14, 2026 update fixed typos in 24 lines, and every translation of those lines fell back to English. Keying by the Yarn line ID alone breaks when the developers re-tag or recreate lines. So LineKey and LineResolver key a line four ways and try the strongest first.

The keys

LineKey computes them, and tools/linekeys.py in the repository computes the same keys with the same definitions. None of them contain the text, so a repository that ships them doesn't ship any of the game's script.

Key Definition Survives
Line ID Yarn's line:xxxxxxxx tag, as DialogueLine.LineId gives it any edit to the text
Hash first 16 hex digits of SHA-256 over the UTF-8 text, exactly as the component receives it (tags included) nothing; this is the key translation packs use
Normalized hash the hash of the normalized text: <tags> removed, ASCII letters lowercased, everything but letters, digits and spaces dropped, runs of whitespace collapsed to one space edits to punctuation, capitalisation, spacing and TextMeshPro tags
Fingerprint a 64-bit SimHash of the normalized text: every 3-character window is hashed with FNV-1a 64 and votes on each bit small edits; similar texts are a few bits apart (LineKey.Distance)

Yarn line IDs are tags the Yarn compiler writes into the script file once, so they stay put when the developers edit a line's text. They only change when a line is recreated or the script is re-tagged.

Resolving a line

LineResolver holds your mod's records (a LineRecord has the keys above, the node and speaker the line was seen with, and any payload you like) and answers Resolve(line, displayedText):

  1. Line ID. If a record has the line's ID, it wins. If the record's hash differs from the text on screen, the match is flagged NeedsReview, since the text changed and the mod's data for it may be out of date.
  2. Hash. This matches the exact displayed text.
  3. Normalized hash. It's only used when exactly one record has it. Texts like "Yes." that several lines share are never guessed.
  4. Fuzzy. Among the records of the same node (or all records when the node is unknown, and none when the node has no records), it takes the one with the nearest fingerprint. That only happens if it's within 10 bits, nothing else is that near (ties are broken by speaker, otherwise refused), and both texts are at least 12 characters after normalization. The match is flagged NeedsReview.

Anything else returns null. The resolver would rather show nothing than the wrong line.

What the numbers are based on

We measured this on the game's 1,839 dialogue lines, using the game's own data and keeping it local (the repository holds only the results):

  • Across the September 14 update, 0 line IDs disappeared, and 24 lines kept their ID while their text changed. The line ID alone would have kept all 24 translated.
  • We edited 300 random lines three ways (a one-character typo, punctuation, capitalisation). The normalized hash and the fingerprint recovered 68% / 93% / 91% of them, and the rest were refused as too short or ambiguous. No edit was matched to the wrong line.

Using it

var resolver = new LineResolver();
// From your data file: keys the tool wrote, plus what you keep for the line.
resolver.Add(new LineRecord { LineId = "line:6046bedf", Hash = "84f325bca745e504", NormalizedHash = "…", Fingerprint = 0x…, NormalizedLength = 10, Node = "Ryan_1_intro", Speaker = "Ryan", Payload = "素晴らしい!" });

GameDialogue.LineShowing += line =>
{
    LineMatch match = resolver.Resolve(line, line.FullText);
    if (match != null)
    {
        Use((string)match.Record.Payload);
        if (match.NeedsReview) Log($"{line.LineId}: text changed, check the translation");
    }
};

To make records from text you saw on screen, use LineResolver.RecordFor(text, lineId, node, speaker, payload), which computes every key for you. Outside the game, python tools/linekeys.py "text" prints the same keys, and python tools/linekeys.py --check checks the Python against ci/linekey-vectors.json. The repository's CI checks the C# side against that same file.

Read operations

Experimental. This came in with framework 1.4.0 and may change or go away in a later version.

Dialogue 1.2.0 registers two read operations in the Operations registry, and you can run them with the Console's op command:

Operation Returns
dialogue.current The conversation now: whether one is running (running), its node, whether options are on screen (options_showing), the last line shown, and whether this game build lets the library see lines and options at all (hooks)
dialogue.recent The last lines and options shown this session (up to 100 are kept), newest last, each with its kind, line ID, node, speaker, where the speaker came from, and text. Parameters: text (only lines that contain it) and max (1 to 100, 20 when left out)

Other members

Member Meaning
const string Guid "com.tomxv.dragnwash.modframework.dialogue"
const string Version The library's version
bool LinesAvailable True when the hooks for spoken lines are installed
bool OptionsAvailable True when the hooks for options are installed
string CurrentNode The Yarn node that started most recently, or null

Do not

  • Patch Yarn Spinner's line or option presenters yourself.
  • Ship the game's script text in your mod or repository. Refer to lines by LineId, or by the stable line keys above.

Clone this wiki locally