Module Triggers(トリガー)

module_triggers.py 冒頭に書かれた指定方法
各トリガーには次のような項目を指定する。
(1) 発動間隔: このトリガーがチェックされる頻度。
(2) 遅延間隔: 条件ブロックが true と評価された後、結果ブロックを実行するまでの待機時間。
(3) 再発動間隔: トリガーの結果を適用後、トリガーが再度アクティブになるまでにどれくらいの時間が経過する必要があるか。
    ここに定数 ti_once を指定すると、一度きり、つまりトリガーが 1 回発動後に再度アクティブにならないようにできる。
(4) 条件ブロック(リスト): 有効な命令のブロック。header_operations.py を参照のこと。
    トリガーがチェックされるたびに、条件ブロックが実行される。
    条件ブロックが true を返した場合、結果ブロックが実行される。
    条件ブロックが空の場合、常に true と評価された と見なされる。
(5) 結果ブロック(リスト): 有効な命令のブロック。header_operations.py を参照のこと。

module_triggers.py が扱う「通常トリガー」は単に「トリガー」とも呼びます。単純トリガーよりも少し複雑で多用途ですが、大した違いはありません。これも やはりワールド・マップにいる時のみ発動します [1]ミッション・テンプレート にも同様の書式でトリガーを記述しますが、そちらはシーンで発動し、時間単位も異なります。(訳注: 原文はもう少し文がありますが、くどいし誤記もありそうなので省略。)

ご覧のとおり、指定すべき項目は単純トリガーよりも多いです。では、一つずつ見ていきましょう。

  • 発動間隔は単純トリガーの場合と同じで、このトリガーが どのくらいの頻度で発動するか、です。
  • 遅延間隔は、発動条件成立後、結果ブロックの実行までどのくらい遅らせるか、です。
  • 再発動間隔は、結果ブロックを適用後、このトリガーをどのくらい長く不活性にするか、です。 (訳注: 前半、原文は「once the consequences are applied」で、結果ブロック開始後か終了後かは やや曖昧。)
  • 条件ブロックには、結果ブロックを発動してよいかどうかを判定するコードを書きます。
  • 結果ブロックは普通、コードの大部分を置く場所です。全条件が true と評価され、かつ遅延間隔が終わると実行されます。 (訳注: 文末付近、原文は「... true and the delay interval wears off」で、順序不問の and に見えますが、上の遅延間隔の説明が正しいなら、条件判定が先。)

さて、前章で示した条件の無い「単純トリガー」を「通常トリガー」で表わすと、どうなるでしょうか?

(24, 0, 0, [], [
  (try_begin),
      (lt, "$player_honor", 0),
      (troop_add_gold, "trp_player", 100),
  (try_end),
  ]),

では、主人公が得る報酬を増やしつつ、他の値も少し書き直して、通常トリガーらしくしてみましょう。

(24, 6, 48, [
    (lt, "$player_honor", 0),
    ], [
    (troop_add_gold, "trp_player", 400),
    ]),

これで このトリガーは、24 時間毎に主人公の名誉を評価します。ゼロ未満なら、主人公を待ちくたびれさせたまま(まぁ、実際には そうではありませんが)6 時間経ってから主人公に 400 ゴールドが与えられます。ただし、このトリガーは次の 48 時間は発動しません。前の例よりも報酬が多くなった分を相殺するためです。そう、このトリガーは まだ改良の余地がありますが、例としては よいでしょう。

「通常トリガー」には(module_triggers.py 以外のファイルに)特別な条件もいくつかあり、そのほとんどは ti_before_mission_startti_on_agent_spawn など、ミッションに関するものです。それらのトリガーは header_triggers.py に一覧されていて、それぞれ使える場所が説明されています。 (訳注: この ti_XXXX は、トリガーの第 1 項目「発動間隔」に指定するので、時間と区別できるよう(ti_once 以外は)負値で定義されています。)

大事な注意点

  1. 単位について、module_triggers.py 内のトリガーに指定する値のうち、発動、遅延、再発動の各間隔の単位は「1 時間」で、module_mission_templates.py 内のトリガーの場合は「秒」です。発動間隔としてゼロを設定すると、トリガーはフレーム毎に起動され、部隊の動きや時間の流れの違いに影響されず、部隊がワールド・マップ上で一時停止した場合(「ポーズ中」と表示中)でも機能し続けます [2]
  2. 特定のトリガー群ばかり多くするのは避けるべき。[3] (訳注: 脚注のリンク先によると、"1, 0, ti_once," のようなトリガーのこと。このリンク先は単純トリガーのページの「廃止されたハード・コーディングのトリガー」にあるリンク先と同じ。)
  3. ローカル変数は、条件ブロックと結果ブロックの間で授受されません。つまり、後で読めるように変数を保存していても、結果ブロック内でその変数を変更したければ、変数の値を作り直す必要があります。 (訳注: 原文は「Local variables are not transferred between conditions and consequences. In other words, you will need to store a variable again to modify it in the consequences if you have already stored it to check it.」。後半の文の modify は明らかに「結果ブロックでローカル変数を書き換える場合」の説明。また、この 2 文にはレジスタなど他の情報が現れないので、2 つの「store」の方向が不明瞭であるものの、「ローカル変数への格納」ではなく、str_store_string の右辺にローカル変数を指定して「ローカル変数の値を読み出して他のどこかへ格納すること」と解釈できそう。だから、前半の文が正しいとしたら、modify するか否かにかかわらず、単に「結果ブロックで それ以前に値を入れたローカル変数を読み出しても値はゴミ。両ブロック間で値を授受したければ、グローバル変数が必須」ということになりそう。)
  4. 特殊な条件を持つトリガーを扱う場合、store_trigger_param_* という命令を使ってパラメータを確認する必要がある場合があります。各トリガーのパラメータの一覧については、header_triggers.py または各行の説明を参照して下さい。

トリガー発動間隔の ぶれ

ゲーム開始からの最初の(単純)トリガー発動が、ややランダムになる。 Leonion, Modding Q&A

トリガーをランダムに遅延させる [4]

これで、トリガーの発動間隔が そもそもランダムに起動していることが判りました。では、意図的にトリガー発動をランダムに遅延させたい場合は どうすればよいでしょうか? 例えば、次のような 24 時間後に 1 回だけ起動するトリガーがあるとします。

(2, 24, ti_once, [], [<your code>]),

例えば、発動は 1 回だけで、起動後 24~48 時間の間でランダムに遅延するものが必要だとしたら、次のように、2 つのトリガーとグローバル変数を使います。

(2,0,ti_once, [(store_random_in_range, "$g_delayed", 24, 49), (assign, "$g_run", 0),], []),

(1,0,ti_once, [(val_add, "$g_run",1), (gt,"$g_run","$g_delayed"),], [<your code>]),

(訳注: 1 段目で単発の通常トリガーを作り、発動間隔は 2 時間、遅延間隔は 0、条件ブロックで「24~48 の乱数を発生して $g_delayed に格納、$g_run を 0 に初期化」し、その判定値は捨てて、結果ブロックも空。2 段目で もう一つの単発の通常トリガーを作っておき、発動間隔は 1 時間、遅延時間は 0、条件ブロックで $g_run を +1 し、それが $g_delayed より大なら発動し、結果ブロックつまり「your code」の区間を実行。)

  • 脚注と出典:
  • [1] 主に次のトピックから採録。 Lumos, Modding Q&A (訳注: リンク先は単純トリガーの脚注 1 と同じ。)
  • [2] kalarhan と RecursiveHarmony, Modding Q&A.
  • [3] kalarhan, Modding Q&A.
  • [4] dunde, Modding Q&A.