NPCがプレイヤーに追従する、フォロワーというシステムがあります。
フォロワーになると、プレイヤーの旅先に同行して一緒に敵と戦ってくれるようになります。非常にありがたいシステムですが、あらかじめフォロワー候補として設定されているNPCしか連れて行かれませんし、すでに他のNPCを連れている場合には断られてしまいます。
今回は、そんなフォロワーの「雇用」に焦点を当てて、Creation Kit (CK) の用語も含め詳しく解説していこうと思います。私がフォロワー拡張MODを作る際、どういった点を考慮しているかについてもお話しします。
1. NPCが追従するのはなぜか
1-1. 追従パッケージ
フォロワーシステムの全貌は複雑ですが、「NPCが追従する」という点に限ってみれば、非常に単純な構造をしています。一言でいうと「クエストのエイリアスについたパッケージでついてくる」だけです。まぁこれでは何にも伝わらないと思うので、具体的に見ていきましょう。
フォロワーを管理しているクエストは「DialogueFollower」というものです。Object Window の Character > Quest の中にあります。
ここで言うクエストとは、ゲーム中にプレイヤーが行うクエストとはやや意味合いが異なり、ゲームシステムで使われるイベント管理のようなものです。NPCやアイテムを登録して動かしたり、それらと同じセルに進入したことを検知してアクションを起こしたりするなど、実に様々なことができます。
このNPCやアイテムなどの登録枠をエイリアスと呼び、枠だけはCKであらかじめ作成しておく必要があります。その中身は、ゲーム中にスクリプトで入れ替えることができます。
フォロワーを登録するエイリアスは、DialogueFollower クエストに「Follower」という名称で作られています。これがいわゆるバニラ枠です。
これを開いてみると、「Alias Package Data」にパッケージが設定されています。「PlayerFollowerPackage」が追従用のパッケージです。
パッケージとは、いわゆる行動AIのことであり、どんな条件の時にどんな行動を取るかという設定です。追従パッケージの中身は示しませんが、常にプレイヤーについてくるだけの非常に単純なものとなっています。
ここで重要なのは、エイリアスにNPCが登録されると、パッケージが自動的に適用される、ということです。スクリプトで対象のNPCをこのエイリアスに登録するだけで、プレイヤーに追従するようになります。
1-2. クエストの優先度
エイリアスへの登録を見ていく前に、クエストの優先度について触れておきます。
1人のNPCは、複数のクエストのエイリアスに登録される可能性があります。例えばフォロワーと結婚式を挙げる際には、フォロワークエストと結婚式クエストの両方に登録されることになります。
複数のエイリアスに入っている場合にどのパッケージで行動するかは、エイリアスが属するクエストの優先度で決まります。フォロワークエスト DialogueFollower の優先度は50で、結婚式クエスト RelationshipMarriageWedding の優先度は90です。すると、優先度の高い結婚式が優先され、伴侶となるフォロワーは結婚式の日に勝手にプレイヤーから離れ、マーラ聖堂へ向かうことになります。
ただし、パッケージには Conditions という条件設定があり、条件に当てはまらない場合にはそのパッケージは適用されません。そのため、まず優先度の高いクエストのパッケージリストを上から順にチェックし、どれも当てはまらなければ次に高い優先度を持つクエストをチェックしていきます。どのクエストのパッケージも適用されない場合には、Actor そのものに設定されている AI Packages が適用されます。
バニラフォロワーの追従を管理するクエストと優先度は、以下の通りです。
- DLC1NPCMentalModel (優先度80) … セラーナ
- DarkBrotherhood (優先度69) … シセロ、および闇の一党の新人
- DialogueFollower (優先度50) … その他の一般的なフォロワー
セラーナさんは、常に専用クエストによってパッケージ管理されています。その優先度は80と高く、思うように管理したい場合には優先度が81以上のクエストのエイリアスに登録しないといけません。だからといって優先度81のクエストでセラーナさんを強制雇用すると、その間は逆にドーンガードクエストの進行に支障をきたします。このあたりの処理が、フォロワー拡張MODで検討しなければならない重要なポイントとなります。
2. 雇用するまでの流れ
2-1. 会話ダイアログの条件
ここからは、一般的なフォロワー候補を雇用する流れを見ていきます。NPCを追従させるには DialogueFollower クエストの Follower エイリアスに入れれば良いので、これを行うスクリプトを何らかの方法で発動させます。
バニラでは、この処理を会話ダイアログを介して行っています。図を用意したのでご覧ください。
一番上にNPCがいます。最下段の3つが雇用形式で、NPCを3つのうちどれかに当てはめれば雇用できると考えてください。バニラの一般的なフォロワーは、会話ダイアログに付けられたスクリプトから DialogueFollowerScript.pex という別のスクリプトを介し、DialogueFollower に当てはめられることになります。
雇用するための会話ダイアログは、DialogueFavorGeneric というクエストで管理されています。その中に、「ついて来てくれ。手を貸してほしい」という選択肢に対し「いいだろう」と答えてくれる会話があります。
これを開くと Conditions に様々な条件が設定されており、すべて当てはまる場合にのみ、この会話選択肢が出るようになっています。
- GetInFaction (PotentialHireling == 0) … 傭兵ファクションに入っていない
- GetIsVoiceType (VoiceFollowerNeutral == 1) … ボイスタイプの指定
- GetGlobalValue (PlayerFollowerCount == 0) … 他にフォロワーを連れていない
- GetInFaction (CurrentFollowerFaction == 0) … その時点でフォロワーではない
- GetInFaction (PotentialFollowerFaction == 1) … フォロワー候補のファクションに入っている
- GetRelationshipRank (>= 1) … 友好度が1以上
なんとまぁ、細かいですね。1つでも当てはまらない場合はこの選択肢が出ません。
ボイスタイプの壁は有名で、これだけはゲームプレイ中に変更することができません。Follower と名の付くボイスタイプリストには以下のようなものがあり、上の会話では、左から2番目の VoiceFollowerNeutral が使われています。
他にフォロワーを連れているかどうかは、Miscellaneous > Global にある「PlayerFollowerCount」を参照して判定しています。バニラの処理では、雇用する際に1、解雇する際に0へ変更されています。
2-2. 会話後のスクリプト発動
会話選択肢のセリフを言い終わると、Scripts 欄の End: に書かれたパピルスフラグメントというミニスクリプトが実行されます。
(pDialogueFollower as DialogueFollowerScript).SetFollower(akspeaker)
スクリプトの構造は割愛しますが、これにより DialogueFollowerScript.pex の SetFollower() 関数が実行され、フォロワーとして雇用されます。akspeaker の部分には会話をしたNPCの情報が入り、誰を雇用するかという情報を渡しています。「この人をフォロワーにしてね」という指示で、当たり前に見えると思うんですが、これが後述する解雇処理とは大きく異なる点です。
また、このパピルスフラグメントが End: に付けられているために、セリフを言い終わらないと処理が実行されません。会話ダイアログはプレイヤーの操作で抜けることができてしまうので、フォロワー拡張MODを作る際はこのタイムラグに注意する必要があります。極端な話をするとセリフの途中で攻撃して敵対することも可能で、そういった場合でも不整合が起こらないようにすることが重要です。
DLCの一部の会話ダイアログでは、パピルスフラグメントが Begin: の方に付けられ、セリフの始まりと同時に処理が実行されるように改善されています。
2-3. DialogueFollowerScript.pex の SetFollower() 関数
DialogueFollowerScript.pex は、クエスト DialogueFollower に付けられているスクリプトです。
その中の SetFollower() 関数では、渡されたNPCの情報を基に、フォロワーとしての雇用処理を行っています。
大事なのは、以下の3行です。
- 33行目:FollowerActor.SetPlayerTeammate() … チームメイトとして隠密行動や抜刀を同期させる。
- 35行目:pFollowerAlias.ForceRefTo(FollowerActor) … DialogueFollower クエストの Follower エイリアスに入れる。
- 36行目:pPlayerFollowerCount.SetValue(1) … フォロワー数を1にする。
33行目と35行目によって、フォロワーとしての行動を取るようになります。36行目でフォロワー数を1にすると、他のNPCと会話しても雇用選択肢が出なくなります。
この33行目と35行目の関数を当てれば、どんなNPCであってもフォロワーにすることができます。上述したクエストの優先度の問題はあるものの、要となる設定はこの2つだけなので、NPCをフォロワー化するというのは全く難しいことではありません。雇用システムの複雑さは、そこに会話ダイアログが絡んで細かい条件設定がなされていることと、後述する解雇処理に難があることに起因します。
2-4. セラーナ式の雇用
セラーナさん自身は入り組んだ制御がなされていますが、雇用形式だけを見ると実は非常に単純です。一般的なフォロワーと干渉するのは「PlayerFollowerCount」の数値のみで、これが0のとき、つまり他にフォロワーを連れていないときに雇用ダイアログが出ます。
セラーナさんを雇用する会話が終わると、セラーナさん自身のクエストが持つ追従パッケージでついてくるようになります。フォロワーになる際、内部的には色々なパラメータが変更されますが、それらはすべてセラーナさんのクエスト内で用いられ、他のフォロワーには干渉しません。DialogueFollower も無関係です。
ほぼ独立した雇用形式と言うことができ、当ページの説明ではこれをセラーナ式と表現しています。シセロと闇の一党の新人もこの形式です。たくさんあるフォロワー追加MODのうち、フォロワー拡張MODの管理下に置かないように指示されているものは、おそらくセラーナ式を採用しています。
3. 別れるほうが難しい
3-1. 解雇させる1行関数
バニラのスカイリムでは、複数のフォロワーを連れていくことはあまり想定されなかったようです。それは解雇処理を見るとわかります。
ゲームプレイ中、フォロワーを解雇するには会話ダイアログを介して行います。この会話は、雇用するときとは違い DialogueFollower クエストで管理されています。
会話後に出されるパピルスフラグメントの解雇指示は、以下のようなものです。
(pDialogueFollower as DialogueFollowerScript).DismissFollower(0, 0)
DialogueFollowerScript.pex の DismissFollower() 関数を呼び出しています。重要なのは、誰を解雇するかという情報が渡されないことです。バニラのフォロワー枠は1つなので、「誰だかわからないけどそこに入っているフォロワーを解雇すればいいよね」という指示になっています。
この解雇処理の問題が、フォロワー拡張MOD最大の難関です。
3-2. DialogueFollowerScript.pex の DismissFollower() 関数
解雇処理となる DismissFollower() 関数は、以下のようなものです。
少し長いですが、雇用システムとして重要なのは以下の4行です。
- 108行目:pFollowerAlias.GetActorRef() … Follower エイリアスに入っているNPCを取得する。
- 111行目:DismissedFollowerActor.SetPlayerTeammate(False) … チームメイト設定を解除する。
- 128行目:pFollowerAlias.Clear() … Follower エイリアスから外す。
- 134行目:pPlayerFollowerCount.SetValue(0) … フォロワー数を0にする。
雇用時に行った設定を解除し、エイリアスから外す処理をしています。これにより、その時点で追従AIから抜け、元の場所に戻っていきます。
108行目がポイントとなる部分で、誰を解雇するかという情報が渡されないため、バニラのフォロワー枠に入っているNPCを解雇対象として取得しています。
3-3. DismissFollower() を呼び出すバニラクエスト
DismissFollower() という関数は、他にも同胞団クエストなどでフォロワーを自動的に解雇する際にも直接呼び出されます。暗黙的に解雇指示が出されるため、解雇処理の構造を変更する場合には、会話ダイアログに加えてこれらに関連したスクリプトとの整合性もチェックしなければなりません。
バニラのクエストで、自動的に DismissFollower() を呼び出してフォロワーを解雇するクエストには、以下のようなものがあります。
- C00 … 同胞団「戦いを始める」
- RelationshipMarriageWedding … 結婚式のウェディングセレモニーシーン
- DLC1RV06 … DLCドーンガード「贈り物」
- BYOHHouseBuilding … DLCハースファイアの執政任命
- DLC2MQ05 … DLCドラゴンボーン「人類の庭師」
これ以外にも、PlayerFollowerCount の数値を変更するなど、フォロワー拡張MODを作る際に影響を与えるバニラクエストが多くあります。関連しているクエストは、クエスト DialogueFollower の右クリックメニューから「Use Info」を開くとわかります。
4. どうやって雇用枠を拡張するか
4-1. 雇用枠の増設
ここからは、雇用枠を拡張する方法として、どのようなものがあるかを考えてみたいと思います。バニラの説明でも出した図の全体像を示します。
NPCを「セラーナ式」、「DialogueFollower」、「MOD専用クエスト」のどれかに当てはめればフォロワー化できます。バニラのシステムは、会話ダイアログを経由したセラーナ式と DialogueFollower になります。
雇用枠を増やすには、DialogueFollower の Follower エイリアスに相当するものを、どこかに増設しなければなりません。
1つの案は、DialogueFollower クエストに直接増設する手段です。エイリアスを複製するなどして枠を増やし、かつ DialogueFollowerScript.pex の SetFollower() 関数を改変して、空いた枠に振り分けるようにします。この案のデメリットは、DialogueFollower クエストに改変を加えるため、フォロワーのAIを改変するMODなどと競合する可能性があることです。また、アンインストールする際に DialogueFollower クエストを停止するわけにはいかないので、セーブデータにごみを残さないようにするのが大変でしょう。
もう1つの案は、MOD専用クエストを用意して、そちらに枠を増設する手段です。専用クエストであれば競合することはありませんし、アンインストールの前にクエスト停止処理を組み込めば設定をクリアできるので、比較的扱いやすいかと思います。このとき、1つあるバニラ枠も併用し、ブレイズのような特殊クエストに対応することも可能かもしれません。
4-2. 雇用枠への出し入れ
雇用枠を増設したら、今度はNPCを出し入れするスクリプトを考えることになります。バニラでは、会話にパピルスフラグメントを埋め込み、DialogueFollowerScript.pex を介して出し入れしていました。
セラーナ式は例外として、一般的なフォロワーは DialogueFollower またはMOD専用クエストのどちらかに入れれば良いので、その経路はいくつもあります。会話のパピルスフラグメントを改変してMOD専用スクリプトで出し入れしたり、あるいは DialogueFollowerScript.pex を改変して振り分け処理を変えたりする方法が考えられます。
複数のフォロワーを雇用するのは比較的簡単な割に、解雇するのはかなり難しい問題となります。バニラでは DismissFollower() 関数に解雇対象が渡されないからです。解決策としては、バニラの解雇用選択肢を無効化してMOD専用のものにすり替えたり、解雇用選択肢のパピルスフラグメントと DismissFollower() 関数を両方とも変更して、解雇対象の情報を渡すようにしたりすることなどが考えられます。
解雇選択肢をすり替える方法は、解雇システムを独自に実装できるので扱いやすいかと思います。音声さえ気にしなければボイスタイプ共通のセリフを使うことも可能で、作業量を軽減できるかもしれません。デメリットとして、カスタムボイスフォロワーの場合、フォロワーMODの作者さんが対応パッチを作る必要性が出てきます。
会話のパピルスフラグメントを直接変更する場合は、関連するすべてのスクリプトを改変しなければならない点が大きなデメリットになります。加えて、DismissFollower() 関数を呼び出すバニラクエストのスクリプトも改変する必要があります。数えたことはありませんが、相当な数のスクリプトに変更を加えることになるでしょう。
一方、会話を介さずに魔法やパワーで直接スクリプトを発動して雇用するようにすれば、どんなNPCであろうがフォロワー化するのは簡単です。雇用処理がオリジナルなら解雇処理もそれに合わせて作れるので、安定した動作が可能になります。
どの方法を用いるにせよ、フォロワー拡張MODを作成する際には、設定を強制クリアする手段を提供すべきです。クリアして初期状態に戻せるのであれば、たとえ何らかのエラーが起きたとしてもセーブデータをだめにしてしまうことはありません。それらのMODを使うユーザー側としても、アンインストールする際には必ず手順通りに行うようにしましょう。
4-3. Simple Follower Extension の仕様
最後に、具体例を2つ示していきたいと思います。まずは Simple Follower Extension です。
このMODでは、会話ダイアログには変更を加えていません。セラーナ式は元のシステムそのままで雇用され、それ以外は会話後に SetFollower() 関数が呼ばれます。DialogueFollowerScript.pex を改変し、雇用するNPCをすべてMOD専用クエストに入れています。バニラ枠は使用していません。
雇用選択肢が制限されないよう、PlayerFollowerCount の数値を事あるごとに0へセットしています。NPCから見ると、常に誰もフォロワーを連れていないと認識されている状態のため、雇用を断られることがありません。
解雇するNPCについては、MOD専用クエストにスクリプトを仕込んで取得しています。解雇する会話を行う際には必ずアクティベートすることになりますので、エイリアスに Event OnActivate() というイベントを付け、最後にアクティベートしたフォロワーを常時取得するようにしています。
この方法のメリットは、会話に変更を加えないために会話ダイアログ拡張MODと競合しないことと、カスタムボイスフォロワーやセラーナ式フォロワーでも相性を気にせず使用できることです。
デメリットとしては、フォロワー候補に設定されたNPCでなければ雇用できない点が挙げられます。
4-4. NPC Utility Menu の仕様
具体例の2つ目は、NPC Utility Menu です。このMODではどんなNPCでもフォロワー化できます。
このMODはパワーを用いる仕様のため、単純にMOD専用スクリプトからMOD専用クエストのエイリアスに入れています。会話ダイアログとバニラの雇用システムには変更を加えないので、そのまま併用することができます。
この方法のメリットは、何といってもバニラのシステムと完全に独立していて競合の心配がないことです。さらに、会話ダイアログと無関係のため、ボイスタイプの壁を気にせずどんなNPCでもフォロワー化できます。
デメリットとしては、雇用関係の会話が楽しめないことと、専用クエストの優先度をいくつにするか悩むことが挙げられます。通常は優先度50で雇えますが、例えばセラーナさんであれば81以上ないとパッケージが適用されません。かといって優先度を高くしすぎると、生活場所を変更するようなMODが動作しなくなったり、他のクエストの進行に支障をきたしたりする可能性があります。NPC Utility Menu では、優先度を4種類用意して切り替えられるようにしています。
以上で終わります。長かったですが、私がお伝えしたかったのは以下の3点です。
- バニラでは、複数のフォロワーを連れていくことは想定されなかったらしい。
- フォロワー拡張MODをアンインストールする際は、必ず手順通りにやろう。
- こんな長いのに読んでくれてありがとう。(一言一句読んでるのは私だけだろうけど)
Credit
アイキャッチ画像には、以下のフォロワーMODを使わせていただきました。素晴らしいMODをありがとうございます。当ページの内容は、こちらにご紹介したMODと直接関係するものではございません。