Module Mission Templates(ミッション・テンプレート)

module_mission_templates.py 冒頭に書かれた指定方法

(訳注: 少し下で後述されるように、このレコード群を含む「mission_templates = [」というリストの定義は、デフォルトでは このソース・コードを 8 % ほど下がった 1275 行目付近から始まります。その手前にあるのは このモジュール内でローカルなトリガーの定義(40 個余り)です。)

ミッション・テンプレートのレコードには下記の項目がある。
(1) ミッション・テンプレート ID (string): 他のファイルから このレコードを特定するために使われる。
    ID の手前に接頭子 mt_ が自動的に付加される。
(2) ミッション・テンプレート・フラグ群 (int): 指定可能なフラグについては header_mission_templates.py を参照のこと。
(3) ミッション・タイプ (int): (廃止)既定のミッション・タイプのうち、どれに属するかを指定。
    (廃止)デフォルトの対話システムで使われるなら、charge か charge_with_ally を、
     それ以外では -1 を指定。
(4) 説明テキスト (string).
(5) 出現点レコード群 (list): 各レコードはタプルで、その内訳は下記 6 項目。
(5.1) 入場点番号。部隊が現れる地点を、番号で指定。
(5.2) 出現点フラグ群
(5.3) 不用装備群。本来の装備のうち、使用させないものを指定。
(5.4) AI フラグ群。
(5.5) 出現人数。
(5.6) 追加装備群 (list)。出現する兵に追加する装備(最大 8 個)。
(6) トリガー群 (list)。
    トリガー全般については module_triggers.py を参照のこと。

この module_mission_templates.py というファイルには、ミッション・テンプレートの一群が含まれています。各ミッション・テンプレートは、簡単に言えば、ミッションと呼ばれる様々なイベントを管理するスクリプト群です。単純なクエストから増援を伴う複雑な戦闘まで、多岐にわたります [1]。戦闘の成り行きをテストしたいような場合、このファイルを変更しながら試行錯誤することになります。

訳注:

ミッション・テンプレートは、シーンと連携します。シーンは「ワールド・マップ以外」での、三人称視点(主人公は下端中央付近に表示される)や一人称視点(R キーで切り替え。主人公視点)で表示される 3D の場面です。シーンではゲーム内時間が進みません。代わりに例えば「戦場」や「闘技場での練習試合(乱戦)」では、プレイヤーのいる実世界の時間を使って秒単位で増援発動などが起きます。あるいは、村で買物をしようとした途端、賊が襲ってくることが時々あるでしょう。それらを行なうのがミッション・テンプレート内のトリガーです。

ミッション・テンプレートと「通常トリガー」は構造が似ていますが、「時間の単位」や「指定できる ti_xxxx」などが異なります。詳しくは本文で後述されます。

MOD 開発での「ミッション」とプレイヤーの「クエスト」を混同しないで下さい。「ミッション」は、主人公自身のための訓練、村人に対する訓練、闘技場での練習試合、戦場での戦闘といった「戦い」のほか、商人との会話、クエストの一部(特定の人物や集団と会うなど)も含んでいます。

ミッション・テンプレートを「ミッション」や「テンプレート」と略すことがあります。単複の区別を考慮して訳しましたが、不明瞭な場合は英語原文を確認して下さい。mission_templates という一つのリスト内に複数のタプルがあり、その一つずつが単体のミッション・テンプレートです。

対立集団との戦闘時には、常に これらのコード・ブロックが調べられます。戦闘中のキー(Tab など)の組み合わせが押された時の処理も設定できます。この module_mission_templates.py 内では、本体のリスト mission_templates の定義が始まる前に、ローカル定数を多数 宣言しています。その内 最初の 2 つの定数は、装備(物品)を変更するためのフラグに使うものです。詳しくは後述しますが、もし あなたがモジュール・ステムの働きの要点をかいつまんで理解しようとしているところなら、それらを読むとよいです。

その後ろの行から本体のリスト mission_templates の手前までは、1 個のタプルとして定義された もっと長い定義が数十個 続きます。例えば common_battle_mission_start などです。これらの形をよく見ると、module_triggers.py のタプルとそっくりです。それもそのはずで、正にトリガーなのです。ファイル冒頭のコメントにある、ミッション・テンプレートの各レコードの第 6 項目「トリガー群」に使います。共通的なトリガーをこのように最初に定数として定義しておくことで、同じトリガーを何度も書かずに済みます。

訳注

上記「ローカル定数」の方法は、ソース・コード上は短くなって可読性も保守性も増しますが、関数呼び出しでなく「マクロ」のようなものなので、当然ながら「疑似コンパイル」して生成される .txt ファイル中には、あちこちで使った数だけ 同じ長い行が 展開されます。C/C++ 系で言うなら、1 つの #define に多数の命令を書き並べるようなものです。.txt が大きくなると、ゲーム開始時の読込時間が増すだけでなく、OS のページ・スワップや CPU のキャッシュし直しによるゲーム実行速度低下に影響し得るはず。だから、(呼ばれる頻度ではなく)何度も展開されるものは、トリガー内のコードだけスクリプト化したほうがよい場合がありそう。)

その後ろ、1275 行目付近の「mission_templates = [」からファイルの最後にある「]」までがミッション・テンプレート本体のリストです。例として、このリスト中、最初にある"town_default"というレコードを、ファイル冒頭の説明に照らして詳しく見てみると、下記のようになっています。

1. ミッション・テンプレート ID(他ファイルから参照時は接頭子 mt_ を付ける必要あり)は "town_default"

2. ミッション・テンプレート・フラグ群(header_mission_template.py で定義されたものを指定できる)は 0

3. ミッション・タイプ(charge か charge_with_ally か -1 を指定)は -1 (この項目は廃止だが何らか指定が必要)

4. 説明テキスト は "Default town visit"

5. 出現点レコード群。このリストと、その中にタプル群として書かれたレコードの 6 個の項目、つまり 5.1 入場点番号, 5.2 出現点フラグ群, 5.3 不用装備群, 5.4 AI フラグ群, 5.5 出現人数, 5.6 追加装備群は、

 [
 (0,mtef_scene_source|mtef_team_0,af_override_horse,0,1,pilgrim_disguise),
...
 (7,mtef_scene_source|mtef_team_0,af_override_horse,0,1,[]),
 (8,mtef_scene_source,af_override_horse,0,1,[]),
...
 (12,mtef_scene_source,af_override_horse,0,1,[]),
 (13,mtef_scene_source,0,0,1,[]),
 (14,mtef_scene_source,0,0,1,[]),
 (15,mtef_scene_source,0,0,1,[]),
 (16,mtef_visitor_source,af_override_horse,0,1,[]),
...
 (31,mtef_visitor_source,af_override_horse,0,1,[]),
 ],

6. トリガー群は、

 [
 (1, 0, ti_once, [], [
   (store_current_scene, ":cur_scene"),
   (scene_set_slot, ":cur_scene", slot_scene_visited, 1),
   (try_begin),
       (eq, "$sneaked_into_town", 1),
       (call_script, "script_music_set_situation_with_culture", mtf_sit_town_infiltrate),
   (else_try),
       (eq, "$talk_context", tc_tavern_talk),
       (call_script, "script_music_set_situation_with_culture", mtf_sit_tavern),
   (else_try),
       (call_script, "script_music_set_situation_with_culture", mtf_sit_travel),
   (try_end),
 ]),
 (ti_before_mission_start, 0, 0, [], [(call_script, "script_change_banners_and_chest")]),
 (ti_inventory_key_pressed, 0, 0, [(set_trigger_result,1)], []),
...
 (ti_tab_pressed, 0, 0, [(set_trigger_result,1)], []),
...
 ],
),

ミッション・テンプレート・フラグ群

ミッション・テンプレートのフラグ群には下記 mtf_XXXX を(複数なら「|」でビット毎論理 OR して)指定します。

mtf_arena_fight (闘技場で)チーム番号ごとに対戦相手たちの ID が決まる。(フラグのビット位置は mtf_team_fight も同じ。)
mtf_battle_mode 持ち物表示抑止。プレイヤーが持ち物画面へ行くキーを押しても、持ち物画面を表示しないようにする。
mtf_commit_casualties (未調査)
mtf_no_blood 血を無効化。このミッション中で血の表現を無効にする。
mtf_synch_inventory このミッションの開始時にプレイヤーの持ち物のバックアップをトリガーし、ミッション終了時にそれを復元する。
mtf_team_fight mtf_arena_fight と同じ。

ミッション・タイプ

Native では次のようなタイプを指定できますが、charge と charge_with_ally を含めて どれも廃止さてていて、何を指定してもゲーム・エンジンに無視されます。(それでも何らか指定が必要です。Native では慣例的に -1 が指定されています。)

全て廃止: cancel_attack, cancel_reinforce, charge, charge_with_ally, intend_battle, leave_during_battle, leave_wo_battle, speak, stay_back, stay_back_with_ally, surrender


出現点レコード

1 つのミッション・テンプレートの第 5 項目「出現点レコード群」は 1 個のリストで、その中の各レコード(出現点レコード)を 1 つのタプルで表わします。下記の例ではリスト中に 4 つの出現点レコードがあります。

 [  ( 1 , mtef_defenders, 0, group(1)|aif_start_alarmed ,8,[]),
    ( 0 , mtef_defenders,  0, group(1)|aif_start_alarmed ,0,[]),
    ( 4 , mtef_attackers,  0, aif_start_alarmed ,8,[]),
    ( 4 , mtef_attackers,  0, aif_start_alarmed ,0,[]),
  ],

この出現点レコードの項目内訳: (入場点番号, 出現点フラグ群, 不用装備群, AI フラグ群, 出現人数, 追加装備群)

(この項 要改善)出現点の説明。 Mirathei, Modding Q&A.。 ミッション・テンプレートのレコードと、出現点のレコード。Khamu, Mount & Blade Modding Discord。 個々の詳細については後続の投稿に説明あり。

入場点と出現点レコード。 kalarhan, Modding Q&A

入場点番号

(訳注: この項は原文に無く、訳者が追加しました。)

入場点番号にはシーン(地形)ごとに予め一つ以上設定した「入場点」の番号を指定します。例えば、このミッションに関連付けられたシーンに 0~9 の計 10 個の入場点があるとして、もし上の例のように 4 つの出現点について各入場点番号を 1, 0, 4, 4 と指定すれば、指定されたトリガー発火時に 10 個のうちの 0 と 1 と 4 に相当する地点からエージェントが出現します。

注意点として、「シーン」に指定した入場点番号と このミッション・テンプレートの「出現点レコード」の先頭項目「入場点番号」は対応しているものの、set_visitors 命令などに指定する入場点番号は、それと関係なくリストのインデクス(0 から)で指定する、ということです。これについては、下記「その他の注意点」の入場点に関する投稿で、La Grandmaster 氏の質問に Waldzios 氏が答えています。

出現点フラグ mtef_

出現する集団が敵か味方か、などを設定します。「フィルタ・フラグ」とも呼ばれます。header_mission_templates で定義されていて、下記のようなものを指定できます。複数を同時に指定するなら、ビット毎論理和「|」で連結します。

mtef_ally_party Native で不使用。(要調査)
mtef_archers_first この入場点で最初の者として出現させる際、弓兵の兵種を優先する。(?)
mtef_attackers 出現させる兵を、対戦を開始した集団の一部とする。(?)
mtef_cavalry_first この入場点で最初の者として出現させる際、騎兵の兵種を優先する。(?)
mtef_conversation_source Native で不使用。(要調査)
mtef_defenders 出現させる兵を、対戦を受けた集団の一部とする。(?)
mtef_enemy_party Native で不使用。(要調査)
mtef_infantry_first この入場点で最初の者として出現させる際、歩兵の兵種を優先する。(?)
mtef_leader_only mtef_no_companions | mtef_no_regulars
mtef_no_auto_reset エントリ ポイントを移動したい場合に指定する必要がある。さもないと、10 フレームごとに「自動リセット」される。[2]
mtef_no_companions 隊にいるコンパニオンが この入場点に出現しないようにする。(?)
mtef_no_leader 各隊のリーダーが この入場点に出現しないようにする。(?)
mtef_no_regulars 一般兵が この入場点に出現しないようにする。(?)
mtef_regulars_only mtef_no_companions | mtef_no_leader
mtef_reverse_order 隊での並び順とは逆の順序で兵を出現させる。(?)
mtef_scene_source 静的に定義された入場点を持つ兵に適用される(module_troops.py のレコードの第 5 項目を参照のこと)。ミッションの入場点に このフラグがある場合にのみ、ミッションの最初のフレームでそれらを出現させる。 [3]
mtef_team_X 出現させる部隊がチーム X に属すようにする。ここで X は 0 ~ 5。
mtef_team_member_2 Native で不使用。(要調査)
mtef_use_exact_number 出現点レコードで指定された「出現人数」通りの兵を出現させる。
mtef_visitor_source この入場点へ訪問者を都度(必要なタイミングで)追加する命令 add_visitors_to_current_scene の使用を有効化 [3]

不用装備群(Alter Flags)

そのミッションで出現する兵の持つ装備のうち、特定の物品を無効化する指定です。例えば、徒歩で戦うスポーツ的なミッションにするなら、馬を登場させたくないでしょう。指定可能なフラグを下記に示します。

af_override_weapons 全ての武器を使用させない。
af_override_weapon_0 装備スロット 0 の武器を使用させない。
af_override_weapon_1 装備スロット 1 の武器を使用させない。
af_override_weapon_2 装備スロット 2 の武器を使用させない。
af_override_weapon_3 装備スロット 3 の武器を使用させない。
af_override_head 兜(ヘルメット)を使用させない。(装備スロット 4)
af_override_fullhelm 兜(ヘルメット)の物品レコードに itp_covers_head (顔を覆う)が指定されているなら、使用させない [4]
af_override_body overrides the armour in equipment slot 5. 胴部防具を使用させない。(装備スロット 5)
af_override_foot 脚部防具を使用させない。(装備スロット 6)
af_override_gloves 手腕部防具を使用させない。(装備スロット 7)
af_override_horse 馬を使用させない。(装備スロット 8)
af_override_everything af_override_all | af_override_foot 全てを使用させない。
af_override_all af_override_horse | af_override_all_but_horse
脚部防具を除く全てを使用させない [5]
af_override_all_but_horse af_override_weapons | af_override_head | af_override_body |af_override_gloves
「脚部防具と馬」を除く全てを使用させない。[5]
af_require_civilian ヒーローの兵種に itp_civilian フラグの付いた物品だけを装備させる。

AI フラグ群

module_mission_templates.py で使用している箇所を読むと、ほとんどの場合、AI が戦闘可能な状態になる aif_start_alarmed に設定されていて、それ以外の場合は 0 です。

出現人数

(訳注: この項は原文に無く、訳者が追加しました。入場点に出現させる(標準の??)人数を指定します。)

追加装備群

ここで出現させる兵に、最大 8 個まで装備を追加します。他の全装備を無効(不用)にすると、ここにリストされているものから選択します。例えば、「木の杖」か「木の剣」のどちらかをランダムに選択させたければ、全ての装備を無効にし、ここにそれらを指定します。

(要加筆?)

トリガー群 [6]

訳注

この web 文書の原文や その原典では、結果ブロックが実行されることを fire(発火)と表現している場合がありますが、実際のゲーム・エンジンは恐らくタイマー割り込みが発火した時の処理に各トリガー(5 個の項目からなる一式)を割り当てているだけのはずなので、この訳文では 「発火」または「発動」という語を、エンジン主体の解釈「5 個の項目からなる一式が使われ始める」という意味で使うことにします。

単純トリガー、トリガー、ミッション・テンプレートの説明。他のセクションに続いている。 Lumos, Modding Q&A.

ミッション・テンプレートに指定するトリガーは、module_triggers.py 内の「通常トリガー」(または単に「トリガー」)と形は同じですが、時間の単位や指定できる ti_XXXX などが異なります。通常トリガーの時間単位は「ゲーム内の世界の 1 時間」ですが、こちらは「プレイヤーの実世界の 1 秒」です。

  • (1) 発動間隔(秒)[7]。このトリガーが どのくらいの頻度で発動(発火)するか 、または ti_on_agent_hit のようなハード・コーディングされたもの。(訳注: ミッション・テンプレート専用の ti_XXXX あり。)
  • (2) 遅延時間(秒)[7]。発動条件成立後、結果ブロックの実行までどのくらい遅らせるか。ただし、ti_on_agent_hit のようなハード・コーディングされたイベントでは機能しない。
  • (3) 再発動間隔(秒)[7]。結果ブロックが完了後、このトリガーを再度有効化するまでの待機時間。ti_once を指定すると、一度だけ発火した(そして条件ブロックが成立なら結果ブロックを実行した)後、二度と起動しないトリガーになる。「発動間隔」が ti_before_mission_start か ti_after_mission start の場合も一度しか発火しないので、この項目に ti_once 併用は不要。
  • (4) 条件ブロック。トリガー発火時に常に実行される。結果ブロックを発動してよいかどうかを判定するコード(一連の命令)を書いておく。
  • (5) 結果ブロック。条件ブロックが成功し(つまり true を返し)、遅延時間を経過後、ここに書いておいたコードが実行される。

簡単な例を示します。

  (10, 2, 60,
    [
     (eq, 1, 1),
    ],
    [
     (val_add, "$global_variable", 10),
    ]),

このトリガーは 10 秒ごとに発火し、条件ブロックに書いた eq という命令が実行されます。この「1 と 1 が等しいかどうか」という命令は常に true になるので、2 秒後に結果ブロックが実行されて、グローバル変数に 10 が加算されます。その後、このトリガーは 60 秒後に再発火し、以降も「条件ブロック実行、結果ブロック実行、60 秒待ち」を繰り返しします。

トリガーの時間指定

時限トリガーの発動間隔/遅延時間/再発動間隔について理解を進めるために、もう少し一般化してみましょう。

(2, 1, 3, [失敗したり成立したりする条件], [何らかの結果処理] ),

このトリガーは、他の「発動間隔が 0 より大きい」全ての時間指定トリガーと同様に、ミッションの最初の 1 秒あたりから発火し始めます。

この条件ブロックは、(戦況やキー操作などによって)「失敗したり成立したりする」ことを想定しています。例えばミッション開始からの時刻(秒)が進むにつれ、下記のように推移したとしましょう。(訳注: この 1 行は訳者が追加。)

時刻(秒) イベント 待ち
1 トリガーが発火; 条件が不成立 発動間隔を適用(+2 秒)
3 トリガーが発火; 条件が不成立 発動間隔を適用(+2 秒)
5 トリガーが発火; 条件が成立 遅延時間を適用(+1 秒)
6 結果ブロックを実行し完了 発動間隔を適用(+2 秒) 再発動間隔を適用(+3 秒)
11 トリガーが発火; 条件が不成立 発動間隔を適用(+2 秒)
13 トリガーが発火; 条件が成立 遅延時間を適用(+1 秒)
14 結果ブロックを実行し完了 発動間隔を適用(+2 秒) 再発動間隔を適用(+3 秒)
19 トリガーが発火....

このように、トリガーの発動間隔を 2 秒と指定していても、偶数秒にだけ発火するわけではなく、実際には、1、3、5、11、13、19 秒目付近で発火します。 (訳注: つまり、もし条件が毎回成立する場合なら、このトリガーの結果ブロックは約 2 + 3 + 1 = 6 秒ごとに実行されます。)

完全に「決められた時間通り」のトリガーが必要なら、遅延時間と再発動間隔をどちらも指定しないことです。

訳注:

上の例を実際に試すには、例えば次のようにします。注意点として、どこかのシーンの中に移る必要があります。マップ上ではトリガーの時間単位がゲーム内の「1 時間」なのに対し、ミッション・テンプレートはシーンと連動していて、時間単位がプレイヤーのいる現実世界の「1 秒」だからです。ここでは Native の既製のシーン scn_dhorak_keep と scn_test2 を利用します。

(1) この web 文書の モジュール開発システムの導入方法 に示した「Hello world」の要領で、module_game_menus.py 内のキャンプ・メニューなどに下記のような選択肢「Test mission 1」を追加します。

( "test_mt1", [], "Test mission 1",
 [
  (set_jump_mission,"mt_test1_1"),
  (jump_to_scene,"scn_dhorak_keep"),
  (change_screen_mission),
 ]
),

これにより、キャンプ・メニューで この選択肢を選んだ時に、指定した最初のシーン(起伏のある草原)へ移り、指定したミッション・テンプレートが有効になります。

(2) module_mission_templates.py の一番下にある「]」の手前の行に下記のように 2 つのミッション・テンプレートを書き加えておきます。それぞれがトリガーを 2 つずつ持っていることを確認して下さい。

  (
    "test1_1", 0, -1,
    "Test 1-1",
    [],
    [
      #---- Trigger 1-1-1
      ( 0, 0, ti_once,
        [],
        [
          (assign, "$my1", 1),
          (assign, reg1, "$my1"),
          (display_message, "@mt1 my1={reg1}"),
          (display_message, "@Hit Tab key to start."),
        ],
      ),
      #---- Trigger 1-1-2
      ( ti_tab_pressed, 0, ti_once, [],
        [
          (reset_mission_timer_a),
          (store_mission_timer_a_msec, reg2),
          (display_message, "@mt1:{reg2}: Tab key was hit. (Hit again to end)"), 
          (set_jump_mission,"mt_test1_2"),
          (jump_to_scene,"scn_test2"),
          (change_screen_mission),
        ],
      ),
    ],
  ),

  (
    "test1_2", 0, -1,
    "Test 1-2",
    [],
    [
      #---- Trigger 1-2-1
      ( ti_tab_pressed, 0, 0, [], [(finish_mission,0)], ),
      #---- Trigger 1-2-2
      ( 2, 1, 3,
        [
          (val_add, "$my1", 1),
          (assign, reg1, "$my1"),
          (store_mission_timer_a_msec, reg2),
          (try_begin),
            (eq|this_or_next, reg1, 4),
            (eq, reg1, 6),
            (display_message, "@mt2:{reg2}: my1={reg1}, cond: passed "),
            (assign, reg1, 1),
          (else_try),
            (display_message, "@mt2:{reg2}: my1={reg1}, cond: failed "),
            (assign, reg1, 0),
          (try_end),
          (eq, reg1, 1),
        ],
        [
          (store_mission_timer_a_msec, reg2),
          (assign, reg1, "$my1"),
          (display_message, "@mt2:{reg2}: conseq. (my1={reg1})"),
        ],
      ),
    ],
  ),

計時用の行(reg1 と reg2 の周辺)は読み飛ばし、全体の構造を読んでみて下さい。ここではメニューで選択直後に「発動間隔」に突入することは避け、Tab キーを契機に実験が始まるようにして計時の精度を上げています。

まずメニューの選択によってシーン scn_dhorak_keep(起伏のある草原)が表示され、このミッションの 1 つ目のトリガーが発火します。ここで Tab キーを押すと、同じミッションの 2 つ目のトリガーが発火、2 つ目のミッションとシーン scn_test2 (城が建っている)を指定してジャンプします。

2 つ目のミッションには、Tab キーでシーンを離脱させるためのトリガーと、本文に出てくる「2, 1, 3, ...」という例を実験するためのトリガーを書いてあります。その条件ブロックを何度か通る中で、本文と同じ「最初の 2 回は不成立、次に成立、その次は不成立、次が成立」という状況を作り出すために、条件ブロックの先頭でグローバル変数 my1 を +1 し、それが 4 か 6 の時だけ条件ブロックの最後の eq が真になるようにしています。my1 は 1 つ目のミッションで予め 1 に初期化しています。

以下は実行結果(画面左下に表示されたメッセージ)の例です。ここで表示される時刻の単位は ms (ミリ秒)で、300 なら 0.3 秒を意味します。mt2 の各行の値に 700 ms を加えてから秒に換算すると、それぞれ約 1, 3, 5, 6, 11, 13, 14, 19, ... となり、本文と一致します。つまり、環境によって、最初の failed までの時間には ばらつき がありそうです。その後の発火時刻が少しずつ(数 ms ~ 十数 ms)増加するのは、ゲーム・エンジンや OS がミッション・テンプレート以外の処理にかける時間、つまりオーバヘッドがあるからでしょう。

mt1 my1=1
Hit Tab key to start.
mt1:0: Tab key was hit. (Hit again to end)
mt2:300: my1=2, cond: failed
mt2:2303: my1=3, cond: failed
mt2:4316: my1=4, cond: passed
mt2:5323: conseq. (my1=4)
mt2:10346: my1=5, cond: failed
mt2:12352: my1=6, cond: passed
mt2:13359: conseq. (my1=6)
mt2:18365: my1=7, cond: failed
mt2:20377: my1=8, cond: failed
mt2:22388: my1=9, cond: failed

同一「発動間隔」の複数トリガー

ひとつのミッション・テンプレート内に、同じ発動間隔(秒指定、または ti_* イベント)を持つ複数のトリガーがある場合、それらの順序が重要になります。そのテンプレート内で最初に書かれたトリガーが最初に発火し、その後、同じ間隔を持つ次のトリガーが発火します。例えば、ti_on_agent_spawn で起動するトリガーが 2 つあれば、上のほう(リスト内の始めのほう)に書かれたトリガーが先に発火します。

ミッション開始付近のトリガー

論理的には、トリガー ti_before_mission_start を指定すると、シーンがセットアップされる前、かつエージェント(人物、馬など)がシーンに出現(スポーン)する前に発火します [8]。 次に、他のトリガーが発火する前にエージェントの出現が起き得ます。この時点で起動し得るトリガーは ti_on_agent_spawn トリガーだけです。次に、ミッション・タイマーの開始時に ti_after_mission_start トリガーが起動され得、発動間隔が 0 のトリガー(全フレーム・トリガー)も起動され得ます。イベント型のトリガー(名前付きトリガー あるいは ti_* トリガー)は、ti_* イベントが発生するまで呼び出されません。他の時間指定型トリガーは、ミッションの最初の 1 秒のどこかで呼び出され始めますが、ti_after_mission_start トリガーと全フレーム(発動間隔 0 の)トリガーより後です。

ミッション開始付近に起動する必要のない(もっと後から発火させたい)トリガーがある場合、条件ブロックに下記のような命令を書くことで、ミッション開始時の CPU 負荷が軽減されます。

(store_mission_timer_a, reg0),(gt, reg0, <チェックを開始する秒数>),

(訳注: 上記は、ミッション・タイマー「a」の値を reg0 に取り出し、それが指定した秒数を超えているかどうか判定しています。この判定をする頻度によっては CPU への負荷が軽くならないはず。)

まとめると、ミッションの開始付近では下記のような順でトリガーが発火することになります。

  • ti_before_mission_start(ミッション開始直前)
  • ti_on_agent_spawn(エージェント出現)
  • (ここがミッション内時刻の起点だとして)
  • ti_after_mission_start(ミッション開始直後)と、0 を指定した時間指定型トリガー(ミッション内時刻が約 0.2 秒のところで発火。)
  • 時間指定型トリガー(この時点では まだ ミッション内時刻が 1 秒以内)
  • イベント型トリガー

(未作成: イベント型ミッション・テンプレート・トリガーの一覧)

トリガーの準ランダム性

準ランダム性(semi-randomness)について。 MadocComadrin and Caba`drin, Modding Q&A

トリガーに関する その他の注意点

ti_on_item_wielded はマルチ・プレイヤーのみ。 Somebody, Modding Q&A。シングル・プレイヤーのミッション・テンプレートでも見かけたものの、現在もマルチとシングルの両方で機能するかどうかは不明。

武器を投げる場合に ti_on_agent_hit トリガーを使うと、武器が人物を突き抜けて複数の人に当たるのが問題。 SonKidd, Modding Q&A。 ある MOD 開発者のバグを別の開発者が機能として取り込むことは、毎度のようにあり得る。

「(0, 0, ti_once, ....」と「(ti_after_mission_start, 0, 0, ....」は ほぼ同じ。 Caba`drin, Modding Q&A

ti_* を指定したトリガーの再発動間隔と遅延時間, cmpxchg8b, Modding Q&A; ti_* トリガーに対し、「再発動間隔 指定あり、遅延時間 指定なし」や「再発動間隔 指定なし、遅延時間 指定あり」は Native で機能しないので、指定不可。 Somebody, Modding Q&A, と kalarhan, Modding Q&A; 時間指定型トリガーとイベント型トリガー(名前付きトリガー)。 Vornne, Modding Q&A.

トリガーの発動間隔。 kalarhan, Modding Q&A.

ミッション・テンプレート間のジャンプ [9]

MOD を開発していると、メニューを経由せずにシーンから別のシーンに切り替えたいことがあるでしょう。例えば、小道具の使用やストーリー(物語)上の条件達成を機に場面を変えたいとか、あるいはプレイヤーが遠征へ参加中に特別なシナリオを組み込みたい、といった場合です。しかし、ミッション・テンプレートの最後で単純に(結果ブロック終了後の行先を指定するような やり方で)そういったジャンプをさせることはできません。理由は、ミッションが終わると、ゲーム・エンジンは(ワールド・マップ上などで)誰かに遭遇した時に最初にロードしたメニューにプレイヤー(の制御対象)を戻すからです。例えば、それが街の場合なら、プレイヤーは街のメニューから やり直すことになります。このように、ミッションを完全に終わらせてしまうと、メニューを経由することになります。

ミッション終了前に別のシーンへ移行するには、例えば下記のように、ミッションのトリガーの条件ブロック(か結果ブロック)に set_jump_mission 命令と jump_to_scene 命令を書きます。そうすれば、プレイヤーは他のミッションへ直接ジャンプできます。

(訳注: このコードは完結しておらず、これだけでは試せません。上の「トリガーの時間指定」の項に示した作例がジャンプを含んでいるので参考にして下さい。)

  (1, 60, ti_once,
  [
    (store_mission_timer_a,reg(1)),
    (ge,reg(1),10),
    (all_enemies_defeated,2),
    (neg|main_hero_fallen),
    (set_mission_result,1),
    (assign,"$battle_won",1),
    (set_jump_mission,"mt_force_conversation"),
    (assign,"$other_char","$other_char_win"),
    (jump_to_scene,"$other_scene_win"),
    (change_screen_mission),
  ],[]),

  (1, 4, ti_once,
  [
    (main_hero_fallen),
    (set_jump_mission,"mt_force_conversation"),
    (assign,"$other_char","$other_char_lose"),
    (jump_to_scene,"$other_scene_lose"),
    (assign,"$player_lost",1),
    (change_screen_mission),
  ],[]), 

戦闘の規模

右の投稿に情報あり。kalarhan, Modding Q&AModding Q&A。 Vetrogor 氏の説明については、GitLab の header_operations 拡張 の問題ノートも参照のこと。

天候

天候に関する情報。 Caba`drin, Modding Q&A

シーンでのランダムな雨。 Roemerboy, Modding Q&A

雨と雪、同時は不可。 Somebody, Modding Q&A

マルチ・プレイヤーでの天候。 Somebody, Modding Q&A

霧の中では AI からの射程と命中精度を下げたい。クロスボウでは既成? Somebody, Modding Q&AModding Q&A

シーンで天候を扱うための基本原理。 Abhuva, Nordous' Sceners Guild

霧に 0 を指定しているのに黒くならず黄色い。 Somebody, Modding Q&A

霧の値が低いのは恐らく黄色い霧の問題のせい? Marko, Modding Q&A (訳注: リンク先に黄色い霧の件は見当たらない。ここの原文は "low fog value probably due to yellow fog problem?")

天候について。 Sim00n (credit), Modding Q&A

シーンのスカイ・ボックスを変更したい(天候かミッション・テンプレートか不明)。 kalarhan, Modding Q&A

スカイ・ボックスに絵を描きたい。 Modding Q&A

マルチ・プレイヤーでの天候。 Namakan (credit), Modding Q&A, と _Sebastian_, Modding Q&A

エディタ内の天候の設定はプレビュー目的。適用するにはコードで書く必要あり。 _Sebastian_, Modding Q&A

天候の設定。 _Sebastian_, Modding Q&A

シーンの霧を無くす。 Dj_FRedy, Modding Q&A

霧に 0 を指定したのに青い。ti_after_mission_start に命令を書く必要がある。 Modding Q&AModding Q&A

set_global_cloud_amount は霧にも影響するようだ。 Khamukkamu, Modding Q&A

スカイ・ボックスの昼。どの時間帯にどのスカイ・ボックスを使用するかを、エンジンがどう決定するか。 Ruthven, Modding Q&A

夜を より暗くするには、霧を暗色にするのが早道。 Pitch, Modding Q&A

シングル・プレイヤーでグローバルな量のもや/雲を生成する方法。 cmpxchg8b, Modding Q&A

武器ダメージ・システム

ヒット判定

ゲーム・エンジンは、ターゲットのヒット・ボックスの 1 つと攻撃者の右の装備品のボーン(骨)との間の衝突をチェックします。後者の場合は、右側の装備のボーンに取り付けられたカプセルを使用します。ゲーム・エンジンは、装備のボーンを見つけるために、名前ではなく、ハード・コーディングされたインデックス 19 を使って参照します [10]

近接武器の弾き返し

プレイヤーが近接武器で相手を攻撃したとき、弾き返されて、力が削がれた経験があるかもしれません。これには下記のような要因が影響します。他にもあるかもしれません [11]

  1. 武器の刃は、線分か細長いカプセルで表わされ、それがエージェントのヒット・ボックスに当たるとダメージを与える。
  2. 武器は次のいずれかの場合に弾かれることがある。
    • 衝突速度が非常に小さい。
    • 相手のヒット・ボックスに対して、刃が(「きりもみ」のようにスピンするなど)変則的な角度で当たる。
    • 武器が充分なダメージを与えない。
    • 柄が相手のヒット・ボックスに当たる。(ゲーム・エンジンが柄をどう認識するかは不明)
  3. 跳ね返りかたは武器の種類と長さにもよる。柄に関して、短い武器にはかなり緩やかであるのに対し、長柄武器は剣の類よりも緩やかではないように見える。ただし、これは観察に過ぎず、未テスト。 (訳注: 原文は "... be a lot more lenience to short ... but ... polearms ... get less lenience ..." で、この lenience を「影響を少なくする」と解釈して訳。)
  4. これらはどれもアニメーションに基づいている。従って、独自の攻撃アニメーションを作って、戦闘モデルに影響を与えることもできる。

キック

キック時、ゲーム・エンジンは蹴り手の正面にいるエージェントを調べます。それは範囲攻撃(AOE, area-of-effect, 周囲の複数名に攻撃が及ぶ)でもあるようです [12]。蹴りの範囲はハード・コーディングされていて変更不可 [13]。エンジンは次のような 3 種類のキックをサポートしています [14]

  1. プレイヤーがブロック中に E キーをクリックすると、アニメーション "prepare_kick_0" が開始。
  2. プレイヤーが前進中に E キーをクリックすると、アニメーション "prepare_kick_1" が開始。
  3. それ以外の場合、アニメーション "prepare_kick_2" が開始。

スクリプトとトリガーを加えることで、"prepare_kick_0" をシールド・バッシュ(盾だけで攻撃)または左パンチに作り替えることができるはずです(両者は同じアニメーションを使い、ヒット・イベントで左手のアイテムをチェックして区別)。その場合でも、"prepare_kick_1""prepare_kick_2" は、引き続き異なる動きの通常キックとして使えます。

ダメージ計算式

ゲーム・エンジンによるダメージ(被害)の計算式は次の通り [15](訳注: 「エンジンによる」という文面から、元々 C/C++ 系で書かれた処理を、説明のため Python に置き換えたコードらしい。いずれにせよ ここでは字下げの数が有意であることに注意。)

if hold_time >= 1.1
    hold_bonus = 1.2
elif hold_time >= 0.6:
    hold_bonus = (1.1 - hold_time) * 0.6 + 1.2
elif hold_time >= 0.5:
    hold_bonus = 1.5
else:
    hold_bonus = hold_time + 1.0

raw_damage = weapon_damage * (clamp(hold_bonus, 1.0, 2.0) * 0.5 + 0.5)

if weapon_type == 'one_handed' or weapon_type == 'two_handed' or weapon_type == 'polearm':
    raw_damage *= math.pow(melee_damage_speed_power, speed_bonus)
elif weapon_type == 'crossbow' or weapon_type == 'bow' or weapon_type == 'throwing':
    raw_damage *= math.pow(missile_damage_speed_power, speed_bonus)

if weapon_type == 'crossbow':
    if raining:
        raw_damage *= 0.75
else:
    raw_damage *= proficiency * 0.01 * 0.15 + 0.85

if weapon_type == 'bow':
    raw_damage *= min(power_draw, difficulty + 4) * 0.14 + 1

    if mounted:
        raw_damage *= horse_archery * 0.019 + 0.8

    if raining:
        raw_damage *= 0.9
elif weapon_type == 'throwing':
    raw_damage *= power_throw * 0.1 + 1.0

    if mounted:
        raw_damage *= horse_archery * 0.019 + 0.8
elif weapon_type == 'one_handed' or weapon_type == 'two_handed' or weapon_type == 'polearm':
    raw_damage *= power_strike * 0.08 + 1.0

    raw_damage += strength / 5.0

    if (weapon_type == 'two_handed' or weapon_type == 'polearm') and (has_shield or mounted):
        raw_damage *= 0.85

        if weapon_type == 'polearm':
            raw_damage *= 0.85

        if weapon_flags & itp_two_handed:
            raw_damage *= 0.9

raw_damage = clamp(raw_damage, 0, 500)

module_items.py 内のアイテムのステータスには最大ダメージ値 255 を割り当てることができますが、ダメージ計算式の最後の行は、通常のダメージの上限が 500 であることを示しています。これは、上記のダメージ計算式が示すように、ダメージにプラス(またはマイナス)の影響を与える他の様々な条件によるものです。例えば、スキル、熟練度、または特定の瞬間における武器の速度などが影響します。この通常の 500 という上限を上回るダメージを設定したければ、トリガー ti_on_agent_hitset_trigger_result を使って優先させたいダメージ値を指定します。

防具への侵襲度(貫通度)の計算式は次の通り [15](訳注: 上と同様、字下げの数に注意。)

armor = appropriate_armor_value_for_hit_location

if hit_shield_on_back:
    armor += shield_resistance + 10

soak_factor = armor * module.ini_soak_factor_for_damage_type
reduction_factor = armor * module.ini_reduction_factor_for_damage_type

if item_flags & itp_extra_penetration:
    soak_factor *= module.ini_extra_penetration_soak_factor
    reduction_factor *= module.ini_extra_penetration_reduction_factor

randomized_soak = (random.random() * 0.55 + 0.45) * soak_factor
randomized_damage = (random.random() * 0.1 + 0.9) * raw_damage
soaked_damage = randomized_damage - randomized_soak

if (soaked_damage < 0.0):
    soaked_damage = 0.0

randomized_reduction = math.exp((random.random() * 0.55 + 0.45) * reduction_factor * 0.014)
reduced_damage = (1.0 - 1.0 / randomized_reduction) * soaked_damage

if (reduction_factor < 0.00001):
    reduced_damage = 0.0

damage_difference = round(reduced_damage + randomized_soak)
effective_damage = randomized_damage - damage_difference

if hit_bone == head:
    effective_damage *= 1.2

    if item_is_ranged:
        effective_damage *= 1.75
elif hit_bone == calf or hit_bone == thigh:
    effective_damage *= 0.9

effective_damage = clamp(effective_damage, 0.0, 500.0)

馬の重傷や致命傷

主人公の乗る「馬」が戦闘中に倒されると、ランダムな確率で重傷(回復可能)になるか死亡する(装備スロットから抹消される)可能性があります。その度合いはゲーム・エンジンにハード・コーディングされていて増減できません。ただし、スクリプトを書いて、ミッション後に馬の「物品」としての修飾子を調べて書き換えるようにすれば、回避はできます [16]

攻撃速度の計算式

武器の速度に関する計算式は次の通り [17](訳注: 上の二つの Python コードと異なり、ここは C/C++ 系。)

float mbAgent::getActionSpeed(int hand, bool reloading)
{
    mbItem item;
    item.m_itemKindNo = -1;

    if (reloading)
    {
        const mbItem *temp = getItem(hand);
        if (temp)
            item = *temp;
    }
    else
    {
        item = getWieldedItem(hand);
    }

    if (!item.isValid())
        return 1.0f;

    mbItemKind *itemKind = item.getItemKind();
    int type = itemKind->getType();
    mbItem secondaryItem = getWieldedItem(ah_secondary);
    float speed = itemKind->getSpeedRating() * 0.01f;

    if (itemKind->isWeapon())
    {
        float weaponFactor = type == itp_type_bow ? 0.11f : 0.07f;

        speed *= 0.01f * (int)getTroop()->getProficiency(itemKind->getProficiency(secondaryItem.isValid())) * weaponFactor + 1.0f - weaponFactor;
    }

    if (type == itp_type_two_handed || type == itp_type_polearm)
    {
        if (secondaryItem.isValid() || hasMount())
        {
            speed -= 0.15f;

            if (itemKind->m_properties & itp_two_handed)
                speed -= 0.05f;
        }
    }
    else if (type == itp_type_shield)
    {
        speed *= g_game->getTroopSkill(m_troopNo, skl_shield, true) * 0.03f + 1.0f;
    }

    if (g_basicGame.isMultiplayer())
        speed *= 0.03f * g_networkManager.m_combatSpeed + 0.94f;
    else if (itemKind->isMeleeWeapon() && (m_no != g_mission->m_playerAgentNo || rglConfig::Battle::iCombatSpeed > 2))
        speed *= 0.03f * rglConfig::Battle::iCombatSpeed + 0.94f;

    return speed;
}

射出速度の計算式

ゲーム・エンジンによる、飛び道具や投擲武器の射出速度(撃ち出すスピード)の計算式は次の通り [18](訳注: 脚注のリンク先にあるように、cRPG という MOD のコミュニティでの情報。下記 2 行目の原文は "PD capped at bow diff+4"。)

ingame_velocity_in_m_per_s = item_kinds_shoot_speed * sqrt((PD * 0.12) + 1.0) * 1.2
{PD(Power Draw, 弓術スキル)の上限は「弓の難度 + 4」で抑えられる}

全ての飛翔体の速度は 1.2 倍になります(理由は不明)。これは add_missile 命令で射出するものを除き、武器の種類とは無関係です。弓と投擲の速度は弓術スキル(Power Draw)または豪投スキル(Power Throw)の影響も受けます [19]

ゲーム・エンジンは飛翔体に「速度の 2 乗に比例した抗力」を適用し、その抗力係数は module.ini 内で下記のように定義します [20](訳注: 「矢」用と「弾丸」用。)

air_friction_arrow = 0.002
air_friction_bullet = 0.002

飛翔速度の計算式

ゲーム・エンジンによる、矢、弾丸、投擲武器の飛翔速度の計算式は次の通り [21](訳注: ここは C/C++ 系。)

float mbAgent::getMissileSpeed()
{
    float missileSpeed = 10.0f;
    float speedFactor = 1.0f;
    mbItem item = getWieldedItem(ah_primary);

    if (item.isValid())
    {
        mbItemKind *itemKind = item.getItemKind();

        missileSpeed = (float)itemKind->getMissileSpeed();

        if (itemKind->getType() == itp_type_bow)
        {
            speedFactor = rglMin(g_game->getTroopSkill(m_troopNo, skl_power_draw, true), item.getDifficulty() + 4) * 0.12f + 1.0f;

            if (m_horseAgentNo != -1)
                speedFactor *= g_game->getTroopSkill(m_troopNo, skl_horse_archery, true) * 0.019f + 0.8f;

            if (g_mission->m_weather.m_precipitationType == wpr_rain)
                speedFactor *= 0.9f;
        }
        else if (itemKind->getType() == itp_type_crossbow)
        {
            if (g_mission->m_weather.m_precipitationType == wpr_rain)
                speedFactor *= 0.75f;
        }
        else if (itemKind->getType() == itp_type_thrown)
        {
            speedFactor = g_game->getTroopSkill(m_troopNo, skl_power_throw, true) * 0.1f + 1.0f;

            if (m_horseAgentNo != -1)
                speedFactor *= g_game->getTroopSkill(m_troopNo, skl_horse_archery, true) * 0.019f + 0.8f;
        }
    }

    return rglSqrt(speedFactor) * missileSpeed * 1.2f;
}

速度と加速度の上限

ゲーム・エンジンによる、飛翔体の最大速度と最大加速度の計算式は次の通り [22]

maxSpeed *=1 ((agility * 0.7f + athletics * 3.0f + 25.0f) * 70.0f / (equippedItemsWeight + wieldedItemWeight * wieldedItemLength * 2.5 + 70.0f) + 90.0f) / 100.0f
maxAcceleration = ((athletics / 6.0f + (agility + 2.0f) / 15.0f) * 40.0f / (equippedItemsWeight + 40.0f) + 1.0f) * 70.0f / (equippedItemsWeight + 70.0f) * 5.0f

武器の持ち替え

人物が ある武器から別の武器に切り替える方法を変更したい場合があるでしょう。例えば、最初に持っていた武器をアニメーションで自動的にしまい、その後で もう一つの武器を引き出すといった場合です。ただし、その動作は Warband にハード・コーディングされています。恐らく唯一の迂回方法は、ti_on_item_wielded トリガーと ti_on_item_unwielded トリガーでアニメーションを再生し、エンジンの処理をバイパスさせることです [23]

弾薬の取捨

弓、クロスボウ、銃を地面に落とすと、弾薬類(矢、ボルト、弾丸)も一緒に捨てられます。これはゲーム・エンジンにハード・コーディングされた動作です。ただし、トリガーを書き加えることで、物を落とした時に、エージェントに新しい弾薬の入った袋を与えるようにすることはできます。ただし、その場合に問題となるのは、何かフラグを設けないと、古いほうの弾薬を拾えてしまう、ということです [24]

その他の注意点

シーンごとの入場点の最大数は 128 で、範囲は 0 ~ 127 。ただし、いくつかのシーン小道具を間に合わせの入場点として機能させる方法がある。 Lumos, Warband Script Enhancer v3.2.0, と cmpxchg8b, Modding Q&AModding Q&AModding Q&A と 追加情報 Modding Q&A.

プレイヤーが戦闘開始時に得るエージェント数の計算式。 Caba`drin, Modding Q&A.

ti_on_agent_dismount トリガー。 Caba`drin と dunde, Modding Q&A。 ti_on_agent_dismount は馬が倒れた時に発火する。 Caba`drin と Mammoet, Modding Q&A, so at both, Caba`drin, Modding Q&A

add_visitors_to_current_scene 命令と entry_point_set_position 命令, Vornne と dunde, Modding Q&A

シングル・プレイヤーでのチーム。 Caba`drin, Modding Q&A

条件ブロックがチェックされるタイミング。 GetAssista, Modding Q&A

mf_battle_mode フラグ, cmpxchg8b, Modding Q&A

ti_on_leave_area トリガーと ti_on_player_exit トリガー。 Caba`drin と Somebody, Modding Q&A

名前付きトリガーでは遅延時間が機能しないが、再武装は正常に機能する。 cmpxchg8b, Modding Q&AModding Q&A

シングル・プレイヤーとマルチ・プレイヤーで使えるチーム数。mtef_team_X は 0~5 しか規定されていないが、Native の闘技場ではプレイヤーがチーム 6 で、他がチーム 0~5。チーム 6~7 を使えるのなら agent_set_team 命令で設定することになりそう。 Somebody, Modding Q&A。 InVain 氏も少しテストしているようなので、要調査。 (訳注: 闘技場や戦場での「戦闘」に絞った議論らしいが、チーム 7 は Native の module_mission_templates.py で普通に使われている。例えば街中やチュートリアルでの人物など。)

ti_on_agent_killed_or_wounded トリガー。 Caba`drin, Modding Q&A

複数トリガーをまとめて変数にする。タプルでなくリストを「+」で連結(ページ全体をよく読むこと)。 Caba`drin, Modding Q&A

シングル・プレイヤーでのチーム。 Caba`drin, Modding Q&A

トリガーのリストを追加。 Modding Q&A

ミッション・テンプレートにトリガーを追加する場合は、いつも先頭に。 _Sebastian_, Modding Q&A

ミッションでのチーム数の最大。 dunde と Caba`drin, Modding Q&A

ti_on_item_dropped はマルチ・プレイヤー専用。 dunde, Modding Q&A

マップ・エディタでの「入場点」を上限 127 より増やせないか。 Lumos, Modding Q&Q

既に説明済みのこと。全ての情報が揃っているか要確認。 Modding Q&A

AI の動きのバグか。初期命令として騎兵に遠隔武器使用を命じたところ、期待通りにならないばかりか、変な動き。右は複数による議論。 Modding Q&A (訳注: この xenoargh 氏の投稿にある "... pretty serious bug with the Agent AI" というリンク先は、サブドメイン bugs.taleworlds.com そのものが 2024 年 4 月現在、タイムアウトして開けないように見える。)

ミッションのトリガーの「条件ブロック」を通過しているのに、「結果ブロック」へ行かないことがある。トリガー数が(60 を超えるほど)多いと、条件ブロックが失敗し始めたり、レベルの始まりで直接起動されたりすることがある。問題を起こすトリガーは「遅延」の追加で回避できることがある。 Swyter と Caba`drin, Modding Q&A

エージェントの出現に関する質問。ミッションの開始時とその後の増援の波で、出現するエージェントがどのように機能するか。 Lav, Modding Q&A

入場点番号は「シーン」と「ミッション・テンプレート」では対応するが、set_visitors など命令ではリスト内インデクス(0 から)で指定する。 Waldzios (credit), Modding Q&A (訳注: entry_no を指定する命令は 12 個あり、その全てをインデクスで指定なのかは不明。)

ミッション・テンプレートで指定したプレイヤーのデフォルトの装備を実行中に変更するには、兵種用の tf_ フラグではなく、ミッション・テンプレートのフラグを mission_tpl_entry_XXXX 命令に指定して実行する。 Lav, Modding Q&A

マルチ・プレイヤーのマップが、クライアントで終了した場合とサーバで終了した場合。 Ikaguia, Modding Q&A

時間の単位は、ミッション・テンプレートの場合(プレイヤーのいる現実世界での 1 秒)とワールド・マップの場合(ゲーム世界の 1 時間)で異なる。 The_dragon, Modding Q&A

複数トリガーの連結。 Somebody, Modding Q&A

ミッション・テンプレートの最後に複数トリガーを追加(複数トリガーの連結)。 kalarhan, Modding Q&A

ミッション・テンプレートに関すること。 kalarhan, Modding Q&A

エンジンが起こすイベント(ti_XXXX)には遅延時間を指定不可。 _Sebastian_, Modding Q&A

ミッション・テンプレートについてハード・コーディングされていることがあれば知りたい。Tavern Animations Pack は ちゃんと動くのに、宿屋のミッションで音楽を変えようとすると うまくいかない? ハード・コーディングうんぬんではなく、town_default と conversation_encounter をよく調べよう。(その後、質問者 自己解決) kalarhan, Modding Q&A

複数トリガーの(リスト同士の)連結。 kalarhan, Modding Q&A

add_reinforcements_to_entry 命令には、ミッション・テンプレートに書かれた入場点ではなく、リストの書かれた順(つまり 0 から始まる通し番号)を指定。 Khamukkamu と kalarhan, Modding Q&A

マルチ・プレイヤーでのチーム追加について。 Vornne, Modding Q&A (訳注: 質問は、Persistent World(PW)というマルチ・プレイ用 MOD でチームを指定したつもりがチーム内で戦ってしまう、という問題。これに対し、質問者の team_set_relation 命令と set_relation 命令に指定している "fac_XXXX" という種族が誤りで、種族はシングル・プレイヤー用、との説明。回答者の示したリンクには PW の情報あり。)

シーンでのセーブやクイック・セーブを抑止。 kalarhan, Modding Q&A

会話時に兜(ヘルメット)を取るようにする。 kalarhan, Modding Q&A

ミッション中の Esc キーなどの抑止。 kalarhan, Modding Q&A

トリガーのリストの連結。 kalarhan, Modding Q&A

mtef_scene_source フラグの目的。reset_visitors 命令を実行した場合でも、特定のヒーローが指定されたシーンとエントリに確実に現れるようにする。 Strange "set_visitor" error

ミッション・タイプ「charge」。 nijis と bryce777 (credit), Breaking the random encounter scene spawnpoints

シーン・ソースの人物は帽子を被らない? Yoshiboy, NPCs not wearing hats

(訳注: 原文では ここに 1 行ありますが、この項の最初のリンクの繰り返しのようなので省略。)

multiplayer_event_XXXX の数の上限 127 はハード・コーディングされているが、common イベントを用意し、最初の int を実際のイベント・タイプとして使うことで迂回可能。 cmpxchg8b, [WB] Warband Script Enhancer v3.2.0

レベルに応じた強さの設定と兵種ツリーは、自動的な戦闘でのバランスに影響。 jacobhinds と kalarhan, Modding Q&A。さらに深入りするなら、 Earendil, Autocalc and Unit Levels の中に示したリンクへ。