Skip to content

Dialogue ja

Tom_XV edited this page Sep 23, 2026 · 6 revisions

English | 日本語

Dialogue ライブラリです。ファイルは DragNWash.ModFramework.Dialogue.dll、名前空間は DragNWash.ModFramework.Dialogue です。これから表示される台詞や選択肢を、Yarn の台詞 ID、話者、ノードといっしょに受け取れます。ゲームの更新で本文が変わった台詞でも、Mod が自分のデータをもう一度見つけられるように手伝います。

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

イベント

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}");

ハンドラーはテキストが出る直前に呼ばれます。どれかが例外を出してもログに書かれるだけで、ほかのハンドラーはそのまま呼ばれます。

DialogueLine

プロパティ 意味
string LineId Yarn の台詞 ID(例:line:6046bedf)。言語が変わっても、誤字修正だけのゲームのアップデートでも変わらない
string Speaker スクリプトに書かれた話者の名前(Ryan)。なければ null。ゲームのスクリプトはほとんどの台詞に話者を書いていないので、ほぼすべての台詞で空になる。下の SpeakerGuess を参照
string Text 名前を除いたテキスト
string FullText スクリプトどおりの名前つきテキスト(Ryan: Hello)
IReadOnlyList<string> Metadata Yarn のタグ(例:lastline)
string Node 分かる場合は Yarn のノード
TMP_Text Component テキストが入るコンポーネント
bool IsOption プレイヤーが選べる選択肢なら true
bool IsAvailable 選択肢のとき、打ち消し線で選べない状態なら false

誰の台詞か:SpeakerGuess

実験的な機能です。 フレームワーク 1.4.0 で入ったもので、この先のバージョンで変わったり、無くなったりするかもしれません。

Dialogue 1.2.0 で、DialogueLine にプロパティが 2 つ増えました。

プロパティ 意味
string SpeakerGuess 誰の台詞か。スクリプトに話者が書いてあれば Speaker、なければノード名からの推測(最初の _ より前。Ryan_1_intro なら Ryan)、選択肢なら Kobold(プレイヤー)。手がかりがなければ null
string SpeakerFrom 推測の出どころ。script、node、option、または null

特定の台詞を変える

テキストを変えるには Text を使い、どの台詞なのかは書き換え処理の中で TryGetLine を使って確かめます。

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

TryGetLine が合うのは、コンポーネントがまだその台詞(名前つきでも名前なしでも、打ち消し線つきでも)を表示している間だけです。そのあとでコンポーネントが表示するテキストには合いません。

台詞の安定したキー

実験的な機能です。 Dialogue ライブラリ 1.1.0(2026-09-17 リリース)で入ったものです。

台詞について何かを覚えておく Mod(翻訳、しおり、チャプターの目印など)は、ゲームが更新されたあとで同じ台詞を見つけ直さないといけません。英文そのものをキーにすると、ちょっと直されただけで見つからなくなります。実際、2026 年 9 月 14 日の更新では 24 行の誤字が直され、その翻訳が全部英語に戻ってしまいました。かといって Yarn の台詞 ID だけをキーにすると、開発元が ID を振り直したり台詞を作り直したりしたときに見つからなくなります。そこで LineKey と LineResolver は 4 種類のキーを持ち、強いものから順に試します。

キー

キーは LineKey が計算します。リポジトリの tools/linekeys.py も、同じ定義で同じキーを計算します。どのキーにも本文は入っていないので、キーをリポジトリに置いてもゲームの台本を配ることにはなりません。

キー 定義 耐えられる変化
台詞 ID Yarn の line:xxxxxxxx タグ。DialogueLine.LineId の値 本文のどんな修正にも
ハッシュ コンポーネントが受け取った UTF-8 の本文(タグ込み)の SHA-256 の先頭 16 桁 なし。翻訳パックが使っているキー
正規化ハッシュ 正規化した本文のハッシュ。<タグ> を外し、ASCII の英字を小文字にし、英数字と空白以外を落とし、連続する空白を 1 つにする 記号、大文字小文字、空白、TextMeshPro のタグの修正
指紋 正規化した本文の 64 ビット SimHash。3 文字ずつの窓を FNV-1a 64 でハッシュし、ビットごとに投票する 小さな修正。似た本文は数ビットしか離れない(LineKey.Distance)

Yarn の台詞 ID は、コンパイラが台本ファイルに一度だけ書き込むタグなので、開発元が本文を直しても残ります。変わるのは、台詞を作り直したときか、台本のタグを振り直したときだけです。

台詞の解決

LineResolver は Mod の記録(LineRecord)を持っています。記録には上のキーと、その台詞を見たときのノードと話者、それに好きなペイロードが入ります。Resolve(line, displayedText) を呼ぶと、次の順に探します。

  1. 台詞 ID。 ID が合う記録があれば、それを採ります。記録のハッシュが画面の本文と違うときは、本文が変わっていて Mod のデータが古いかもしれないので、NeedsReview を立てます。
  2. ハッシュ。 表示される本文そのもので探します。
  3. 正規化ハッシュ。 合う記録が 1 つだけのときに使います。「Yes.」のように複数の台詞が同じ本文を持つものは、推測しません。
  4. あいまい照合。 同じノードの記録の中から(ノードが分からなければ全記録から、ノードに記録がなければ照合しません)、指紋が一番近いものを採ります。ただし、差が 10 ビット以内で、その近さのものが 1 つだけ(同点なら話者で絞り、それでも決まらなければ断ります)、しかも両方の本文が正規化後に 12 文字以上あるときだけです。この場合も NeedsReview を立てます。

それ以外は null を返します。間違った台詞を出すくらいなら、何も出さないほうを選びます。

数字の根拠

ゲームの台詞 1,839 行で、ゲーム自身のデータを手元だけで使って測りました(リポジトリに置いているのは結果だけです)。

  • 9 月 14 日の更新の前後で、消えた台詞 ID は 0 件、ID はそのままで本文が変わった台詞は 24 件でした。つまり台詞 ID だけでも、24 件すべての翻訳を保てたことになります。
  • ランダムに選んだ 300 行に 3 種類の修正(1 文字の誤字、記号、大文字小文字)を加えてみたところ、正規化ハッシュと指紋で 68% / 93% / 91% を取り戻せました。残りは、短すぎるか紛らわしいとして断っています。別の台詞に間違って当てたものは 0 件でした。

使い方

var resolver = new LineResolver();
// データファイルから:ツールが書いたキーと、その台詞について覚えておくもの。
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}: 本文が変わっています。翻訳を確認してください");
    }
};

画面で見た本文から記録を作るときは、LineResolver.RecordFor(text, lineId, node, speaker, payload) を使えばキーを全部計算してくれます。ゲームの外では python tools/linekeys.py "本文" で同じキーが出せて、python tools/linekeys.py --check で Python 側が ci/linekey-vectors.json と合っているか確かめられます。リポジトリの CI では、C# 側も同じファイルで確かめています。

読み取りの操作

実験的な機能です。 フレームワーク 1.4.0 で入ったもので、この先のバージョンで変わったり、無くなったりするかもしれません。

Dialogue 1.2.0 は 操作の登録簿 に読み取りの操作を 2 つ登録していて、Console の op コマンドで動かせます。

操作 返すもの
dialogue.current 今の会話:会話が進んでいるか(running)、そのノード、選択肢が画面に出ているか(options_showing)、最後に出た台詞、このゲームのビルドでライブラリが台詞と選択肢を受け取れるか(hooks)
dialogue.recent このセッションで最近出た台詞と選択肢(最大 100 件を保持)。古い順で、それぞれ種類、台詞 ID、ノード、話者、話者の出どころ、本文つき。引数は text(それを含む台詞だけ)と max(1〜100。省略すると 20)

そのほかのメンバー

メンバー 意味
const string Guid "com.tomxv.dragnwash.modframework.dialogue"
const string Version ライブラリのバージョン
bool LinesAvailable 台詞のフックが入っていれば true
bool OptionsAvailable 選択肢のフックが入っていれば true
string CurrentNode 直近に始まった Yarn のノード。なければ null

やめてほしいこと

  • Yarn Spinner の台詞や選択肢の表示部品に自分でパッチを当てること
  • ゲームのスクリプトの文章を Mod やリポジトリに含めること。台詞は LineId か、上の安定したキーで指定してください

Clone this wiki locally