Papyrusログに MHP_ConfigQuest のスタックが溜まって落ちる人へ

私的経緯

例えば歩いているだけで錬金素材が集まるMODなんかを入れていると、
「持てるだけ持ち歩いて後でまとめて錬金しよう」とか思うわけです。
持ち歩ける重量が多ければ多いほど、のちのち「山の青い花 (280)」とかになる。
ちなみに私が愛用させてもらっているのはこちら。

当然300回近く R キーを押すなんて人間には至難の業で、
漏れそうな下を必死こいた気分で我慢しながら駅のトイレのドアを叩くような事態に陥るよりも、
きっとこういう連打ツールを使いたくなるはずなのです。

しかし使ってみるとわかるんですが、人智を超えたスピードでアイテムが作成されるため
Papyrusスクリプトエンジンには途方も無い負荷がかかります。
いや、本来は(多分)スクリプト関係ないんだけど、MODによってはあるようで。

MHP = More HotKeys Please

タイトルにある MHP_ConfigQuest は比較的有名かつ高機能なホットキーMODである*1

に含まれている MHP_PlayerScript.psc に含まれているものです。

上記ブログで言及されているように、More HotKeys Please(以下MHP)は
インベントリへのアイテムの追加、およびアイテムの削除をモニタリングしているので、
一度の錬金につき「一つ目の材料削除」「二つ目の材料削除」(オプション材料削除)「薬の追加」という
少なくとも合計3つ以上のイベントが同時に発生することになります。

原因はスクリプトの過負荷

さらにそのイベント内で深くネストされたIf - EndIfWhile - EndWhileブロックが大量にあって、
Papyrusコード的には結構な負荷がかかります。落ちても仕方がないレベルと言ってもいいです。
これはホットキーにポーションを選択したときの<mag>を計算している――
つまり同じ名前の薬のうちどれを優先的に使用するかの判断をしているわけですが、
個人的に薬は[http://skyrim.2game.info/detail.php?id=29512:title=SwiftPotionReborn]を使っているのであんまり必要ありません。

ので、とにかく落ちないようにするためにスクリプトを書き換えてみました。
需要があればコンパイル済みの MHP_PlayerScript.pex もどこかに置きますが、
MHPの作者さんに申し訳が立たないので多分やりません。コードのみ。

スクリプトの書き換え準備

バージョンは More HotKeys Please v3.0 です。

必要なもの:

  • [http://skyrim.nexusmods.com/mods/23416/?:title=MHP]スクリプトのソース。デフォルトで Data\Scripts\Source\ に展開済み。
  • [http://skyrim.nexusmods.com/mods/3863/?:title=SkyUI]スクリプトのソース。デフォルトでは展開されないので、[http://wiki.skyrim.z49.org/?MOD%2FTool#bsaunpacker:title=BSA Unpacker]を使って取り出し・配置すること。
  • [http://skse.silverlock.org/:title=SKSE]スクリプトのソース。インストーラを使った場合は展開されていないので、アーカイブから Data\Scripts\Source に放り込む。
  • [http://wiki.skyrim.z49.org/?MOD%2FTool#h952bf0b:title=Creation Kit]

スクリプトコンパイルCreation Kit 本体は使いません。
くっついてくる Papyrus Compiler というものを使います。
\SteamApps\common\Skyrim\Papyrus Compilerに存在が確認できるはず。
無ければそこはプレイ用のSkyrimフォルダなので、Creation Kitをインストールしたフォルダを漁りましょう。

発見できたら、デスクトップとか適当なところに簡単な .bat ファイルを作ります。
ドラッグ&ドロップでスクリプトコンパイルしてくれる親切設計ですが、
CK をインストールしたフォルダとプレイ用 Skyrim フォルダを把握しておく必要があります。
が、今回は同一フォルダにインストールされているものとしましょうね。*2

compile_pupyrus.bat:

"D:\SteamLibrary\steamapps\common\skyrim\Papyrus compiler\PapyrusCompiler.exe" %1 -f="TESV_Papyrus_Flags.flg" -i="D:\SteamLibrary\steamapps\common\skyrim\data\scripts\source" -o="D:\SteamLibrary\steamapps\common\skyrim\Data\Scripts"
pause

D:ドライブの直下にSteamライブラリフォルダがあると仮定します。
場合によっては C:\Program Files(x86)\Steam\steamapps\... のようになっている可能性もあります。

PapyrusCompiler.exe は第一引数にスクリプトもしくはフォルダを取り、
-i にソーススクリプトの場所を指定、 -o コンパイル済みスクリプトの出力先を指定するようです。
-f 引数についてはよくわかりませんが、あまり深く触らないほうが良さそうなのでおまじない。

スクリプトの改竄

MHP_PlayerScript.psc をどこか適当なところ(やはりデスクトップとか)にコピーし、適当なエディタで開きます。
このとき Data\Scripts\Source にあるものを直接編集してしまうとオリジナルが行方不明になるので、
確実にコピーを取るようにしてください。

それから次の行を探します。

  • Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
  • Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)

v3.0 では81行目、174行目にありました。
見付けたらそれぞれ2箇所編集します。 ; 記号から後ろはコメントとみなされて無視されますが、
日本語が入っているとコンパイルに失敗する可能性があるかもです。

まずは MHP_PlayerScript.psc: (OnItemAdded付近)

Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
	If akBaseItem.gettype() == 46
		If Writer.PotionEffects.length < 58
			Writer.PotionEffects = New MagicEffect[58]
			int i = 0
                        ; この下をいじくる
			; while i < Writer.PotionEffects.length ←コメントアウト
			while i < 0 ; 追加
				Writer.PotionEffects[i] = writer.MHP_AlchEffects.getat(i) as MagicEffect
				i += 1
			endwhile
		endif
		potion tempPotion = akBaseItem as Potion
		int count = tempPotion.getnumeffects()
		int j = 0
                ; この下もいじくる
		; while j < count ←コメントアウト
		while j < 0 ; 追加

それぞれ 0 を指定するとポーションの使用に支障をきたす可能性があるので、
必要に応じて数値を変更してください。具体的な目安はわかりませんが、5から10など。
数値が大きくなればなるほど負荷が大きくなります。
逆に 0 を指定した場合はループが一切回らなくなるので、負荷はほとんどかからなくなります。

続いて OnItemRemoved ですが、こちらも同じようなコードを2箇所変更します。
作用・副作用については上記と同じ。
MHP_PlayerScript.psc: (OnItemRemoved付近)

Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
	If akBaseItem.gettype() == 46 && player.GetItemCount(akBaseItem) <= 0
		If Writer.PotionEffects.length < 58
			Writer.PotionEffects = New MagicEffect[58]
			int i = 0
                        ; この下をいじくる
			; while i < Writer.PotionEffects.length ←コメントアウト
			while i < 0 ; 追加
				Writer.PotionEffects[i] = writer.MHP_AlchEffects.getat(i) as MagicEffect
				i += 1
			endwhile
		endif
		potion tempPotion = akBaseItem as Potion
		int i = 0
                ; この下もいじくる
		; while i < Writer.KeyForm.length ←コメントアウト
		while i < 0 ; 追加

コンパイルと設置

あとはもうコンパイルするだけです。
といっても簡単で、先ほど改竄した MHP_PlayerScript.psc compile_pupyrus.bat にドラッグ&ドロップするだけ。
コンパイルに必要なファイルが自動的にロードされ、即座にコンパイルが始まります。

Starting 1 compile threads for 1 files...
Compiling "MHP_PlayerScript"...
Starting assembly of MHP_PlayerScript
0 error(s), 0 warning(s)
Assembly succeeded

Compilation succeeded.

Batch compile of 1 files finished. 1 succeeded, 0 failed.

こんな感じの文字が表示された黒いウィンドウが出るはずです。
もし Batch compile of 1 files finished. 0 succeeded, 1 failed. と表示されている場合は
コンパイルに失敗しているので、必要なファイルがあるかどうか確認してください。

SKSE, SkyUIスクリプトのソースが展開されているかどうか、
compile_pupyrus.bat に記述されている -i= のフォルダが正しいかどうか、などです。
Batch compile of 1 files finished. 1 succeeded, 0 failed. と表示されていれば成功しているので、
何か適当なキーを押すか、右上の☓ボタンでウィンドウを閉じてしまって大丈夫です。

確認

さて実際にスクリプトが更新されたのかどうかですが、
Data\Scripts フォルダを開いて日付順に整列させればすぐに分かります。
直近に更新されたファイルが MHP_PlayerScript.pex になっていれば成功です。

もしそうでなければコンパイルに失敗しているか、
どこか別のところにスクリプトが出力されている可能性があります。
compile_pupyrus.bat-o= のフォルダを確認してみてください。

ゲームの起動

いつも通り起動してしまって問題ないはずです。
ただし動いているのは改竄済みのスクリプトなので、
ポーション関係、あるいはまったく別のところでおかしな動きをしても、自己責任ということで。

私への質問などは構いませんが、間違っても More HotKeys Please のフォーラムに
バグ報告などはしないようにしましょう。もっとも、私も MHP について
完全に把握しているわけではないので、これでいいのかどうかもまだわかりません。

ご利用は計画的に。

*1:最初はGRHotkeys使ってたんだけどScriptDragonの導入が面倒になってやめてしまったよ…。

*2:なぜなら解説が面倒だから。