module_strings.py は MOD システムのソースの中で一番わかり易いファイルかも。「テキスト」の多くがここに集まっていて、様々な方法で表示されます。これらの文字列は、module_dialogs.py や module_quests.py といった、テキストの一群を画面に表示する必要があるような あらゆる場面で使われます。ただ、module_strings.py は、単一ファイル内向けの文字列ではなく、独立していて共通に使われる文字列の置き場所です。だからモジュール・システム内のどこからでも「命令」を使って呼び出せます。これらの文字列は、画面の左下でスクロールするメッセージとして表示されたり、チュートリアル・ボックスに表示されたり、ゲーム・メニューや一連の会話に使われたりするなど、ゲーム内のあちこちで見ることができます。
module_factions.py と同様に、このファイルは Python 言語のリスト「strings = []」という形をしています。最初の数行のタプルはゲーム・エンジンにハード・コーディングされた処理から使われるので、「Strings before this point~」と書かれた行の手前を変更してはいけません。その下を見ていくと、例えば次のような文字列があります。
("bandits_eliminated_by_another", "The troublesome bandits have been eliminated by another party."),
あるいは下記のようなものもあるでしょう。
("s5_s_party", "{s5}'s Party"),
これらタプル内の要素は次の順で指定します。
(1) 文字列 ID。他のファイルから このレコードを特定するために使われる。
(2) 文字列の内容。
文字列での注目すべき機能の 1 つは、数値や別の文字列をその中に埋め込むことができることです。数値を埋め込むには数値レジスタを使って、例えば、文字列中に {reg0} のように書きます。これにより reg(0) の現在の値が使われて表示されます。{reg10} なら reg(10) の値だし、その他も同様です。
数値でなく文字列を埋め込むなら、文字列レジスタを使います。例えば、{s2} は、文字列レジスタ 2 番の内容を表示します。文字列レジスタと数値レジスタは独立していて、上書きなどの干渉を起こさないので、目的毎に同時に使い分けることができます。
文字列レジスタは次のように使います。
- 様々な str_store_xxxx 命令が header_operations.py で定義されていて、それらを使って値を設定。クリアするには str_clear 命令。
- display_message 命令や会話文に指定する文字列内で、{s#} という表記で参照。
例えば、module_strings.py ファイルで次のようなレコードを定義したとします。
("lets_meet_in", "Let's meet in {s10}."),
どこかのトリガーや条件ブロック、結果ブロックなどに下記コードを書きます。2 命令目で上記レコードを使っています。他ファイルから string モジュールのレコードを参照するので、lets_meet_in という ID の前に接頭子 str_ を付けます。同様に 1 命令目では party(集落や施設)の接頭子 p_ を付けた zendar という ID を指定しています。
(str_store_party_name, 10, "p_zendar"),
(display_message,"str_lets_meet_in"),
上記 str_store~ の行が実行されると、p_zendar に対応した文字列 Zendar が文字列レジスタ 10 番に代入されます。続く display~ が実行されると、ゲーム・エンジンはパラメータが上記最初に示した string モジュールのレコードであることを見つけ、{s10} の箇所を「実行時の文字列レジスタ 10 の内容」に置き換えて左下メッセージとして表示します。
Let's meet in Zendar.
(訳注: 上記の原文は かなり省略されているので、少し補って訳してあります。接頭子などの規則については、この web 文書の各モジュールの説明にあります。また変数の指定方法は、トップページの導入部にある「開発言語の書式」などにも説明があります。)
文字列と書式 [2]
文字列を扱う際、いくつかの特別な構文ルールに留意する必要があります。
「^」はゲームの実行時にテキスト中に強制的に改行を挿入する指示です。一方、「\」(日本語環境での「¥」はブラウザによってはバックスラッシュに見えるかも)は、(他のプログラミング言語のソースでもできる方法として)ソース・コード中でだけテキストを複数行に分けて書きたい場合に行末に置いて その後ろで改行することができます。実行時にはその改行は認識されません。その場合の行末の「\」は単に人がコードを読みやすくするためのものです。その改行直後の行頭に余分なスペースを入れることはできず、入れると構文エラーが発生します [3]。
「{!}」は空の文字列のためのプレースホルダーとして、つまり出力用に用意された場所として使われ、ゲームの実行に必要です。通常、後ろにレジスタまたは文字列が続きますが、それらは null になる可能性があり、正しく表示されません。また、翻訳の .csv ファイルの原形を生成する時に、行の生成対象からも除外されます [4] (訳注: 英語版の *.csv を生成する機能が Warband に備わっています。その概要を この web 文書の導入部の「モジュール構造だから翻訳もし易い」に注釈しました)。 それは、固定の文字列(部隊名など)の一部でない限り、ゲーム内で表示されません。
「_」(アンダースコア)は、文字列内では空白として扱われます(物品や兵種などの名前でも同様)。例えば、2 つの文字列 ("cant_use_inventory_now","Can't access inventory now.") と ("cant_use_inventory_now","Can't_access_inventory_now.") の表示結果は同じです。
文字列中の {reg#} は、当該レジスタ値に置き換えられて出力されます(ここで # は数字)。下記は、文字列内でグローバル変数の数値を使う例です。
(assign, reg1, "$g_your_global_variable"),
(str_store_string, s1, "@The value of the global variable is {reg1}."),
(str_store_string, s0, s1),
「@」を先頭に付けたものはクイック文字列(その場限りの文字列)で、module_strings.py で宣言せずに使えます。
(訳注: 通常の文字列とクイック文字列は、「意図的に共通化するか否か」で使い分けます。後ろの項にも少し説明あり。なお、上記コードでは、まずグローバル変数の値を読んで reg1 に代入し、その値を次行の文字列に埋め込んで s1 に代入、さらに それを s0 にコピーしています。命令 assign は数値のみ扱い、str_store_string は文字列のみ扱います。)
「{reg#?str1:str2}」という構文があり、reg# が true かどうかを調べ、true なら str1 が、そうでなければ str2 が使われます。ここでの reg# の値は単純なブール値で、1 が true、0 が false を意味します。
(訳注: これらのうち、強制改行「^」や 3 項演算子「{a?b:c}」は、ゲーム・エンジンが実行時に解釈するので、翻訳版の csv 内でも使えます。例えば、会話文や左下メッセージやクエスト文などでは、半角空白が自動改行の候補になりますが、文によって改行位置を指定したい場合は csv 内でも「^」で強制改行できるし、あるいは話者の性別ごとに女性語と男性語を表示し分けたい場合、性別判定のためのレジスタ番号がわかっていれば、csv 内でも 3 項演算子を使って可能です。後述の「{a/b}」という性別判定方法もありますが使用箇所によっては機能しないように見えます。一方、グローバル変数名や命令名などは「コンパイル」時に数値(ハッシュやオペコード)に置き換えられて txt ファイルだけで使われ、翻訳用の csv ファイル内では使えません。reg# や s# (この # は数字)は csv でも使えるので、例えば(自分や他人の)バグらしきものに出くわした時に、再コンパイルせずとも csv に一時的に手を加えて全レジスタを表示するなどして追跡することもできます。)
性別判定付き置換
この項は未整理。(訳注: 英語原文が意味不明なので、見出しの title 属性の記述を参考に訳。見出しに含まれている skin は参考リンクにしか現れず、かつアニメーションに関することなので、見出しから省いて訳。)
(訳注: ここの英語原文 2 段落は上述と同じ内容なので訳を省略。)
会話文では、対象者の性別に応じて文字列の内容の一部(または全て)を異なる表示にすることもできます。例えば、文字列に {sir/madam} を挿入すると、宛先が男性の場合は「sir」が表示され、女性の場合は「madam」が表示されます。
これらの記述方法は全て(性別判定付きの置換を除いて)、あらゆる種類の文字列の項目で完全に機能します。性別判定付きの置換は会話の中でのみ機能します。
display_message という命令では、16 進数のカラーコードを指定して文字列を着色できます。(display_message,
Gender at Dialogues (訳注: 原文にリンク無し)
性別を定義するスクリプトに変更(注意深く読んで、MS での改善を考慮)。 dunde, Modding Q&A
新しい種族の性別の問題。 Somebody, Modding Q&A
会話での性別判定。 Somebody, Modding Q&A
新しい種族の性別。 Lumos, Modding Q&A
性別判定の 3 項演算子を {a?b:c:d} のようには書けないが、{a?{b?c:d}:{b?fe:f}} のように入れ子にはできる。 Lumos, Modding Q&A と Modding Q&A, および Somebody, Modding Q&A
会話文の性別判定, gsanders (credits), Modding Q&A
新しい種族の性別。 kalarhan, Modding Q&A
スキンと性別判定。 kalarhan, Modding Q&A (訳注: アニメーションに男性スキン用フレームと女性スキン用フレームを用意する件については、この web 文書のトップページ前半→「BRF バイナリ・リソースの扱い」のページで女性に関する記述を参照のこと。)
文字列での男女。 様々な発言者による, Male races
strings と quick_strings の違い [6]
クイック文字列は、module_strings.py で事前に定義されない、その場で定義される(ソース・コードに書く一過性の)文字列です。文字列 ID が通常使われる場所ならどこでも(通常は文字列レジスタ向けに)使えます。クイック文字列は固定文字列とほぼ同じように記述されますが、先頭に「@」が付きます。だから普通の文字列を「str_strings」、クイック文字列を「@strings」のように呼び分けることがあります。コンパイルするとクイック文字列は quick_strings.txt に配置されますが、普通の文字列のような参照のされ方は されません。なぜクイック文字列を使うのでしょうか? なぜモジュール・システムはそれを単に文字列としてメモリ上に置かないのでしょうか? それは、一過性のメッセージ表示の場合、ソース・コードにその場で定義できるクイック文字列の方が どう考えても(開発者にとって)ずっと便利だからです。通常の module_strings.py 内の文字列では、順序(とインデクス)が重要で、スクリプトが先頭行からの行数(文字列 ID オフセット)と他の ID(シーン ID など)を関連付けます。Python のリストとしてメモリ上に置かれるのは通常の文字列のほうだけです。と言っても、通常文字列とクイック文字列は、どちらも「コンパイル」されます。内部的には両者の違いは ありません [7] (訳注: つまり実行時、ゲーム・エンジンからすれば どちらの場合も結果的にインデクスを使って txt や csv を読む、という点で同じ)。
「str_」は他所から参照できますが、@ はできません。文字通りそれが唯一の違いです。何度か使い回す予定が無く、その場限り(fire and forget, ファイア・アンド・フォゲット)の文章にする予定なら「@」を使います。他で使う予定がありそうなら「str」として登録して使うとよいでしょう [8]。
訳注:
すぐ上の段落の「他所うんぬんが唯一の違い」は、語弊があります。クイックでないほうの文字列には「連続した複数レコードを配列のように扱える」という重要な特徴があります。
例えば、scripts モジュール内の "lord_comment_to_s43" というスクリプトがその一例です。そこでは、第 2 引数に渡ってきた文字列 ID に何らかの数をインデクスとして加算することで ID をずらしています。その結果、連続する複数の文字列 ID から一つを選んでアクセスしています。
その例では特定の兵種スロットの値を、ID ずらしのためのインデクスに使っています。MOD によっては これを乱数に置き換えることもできるでしょう。いずれにしても、扱う複数 ID の総数(ずらす最大幅)と処理の不整合を無くしつつ、可読性を保つために、例えば文字列レコード側の ID 名に通し番号を含めたり、処理側で使うローカル変数のとりうる範囲が その名から判るようにしたり、といった工夫する必要があるでしょう。
その他の注意事項
文字列の比較、サードパーティによるツールが必要。 The_dragon, Modding Q&A
文字列の最大長。 cmpxchg8b, Modding Q&A
module_strings に直書きした文字列。 Lav, Modding Q&A
変数値を文字列に連結する。 Arch3r, Modding Q&A
文字列をグローバル変数に入れる。 kalarhan, Modding Q&A
変数の数値を文字に変換。 kalarhan, Modding Q&A
- 脚注と出典:
- [1] Neophyte, Modding Q&A.
- [2] Bundeling notes of jik, Updated Module System Tutorial (page 20), WookieWarlord, Modding Q&A, と Somebody, Modding Q&A と Modding Q&A.
- [3] kalarhan, Modding Q&A.
- [4] Somebody, Modding Q&A.
- [5] Swyter, Modding Q&A.
- [6] 断りの無い限り、古いサイト M&B Modding Wiki から。
- [7] Lumos と cmpxchg8b と Vornne による議論のまとめ, Modding Q&A.
- [8] Dalion, Mount & Blade MOdding Discord.