ElepanSkyrim’s めも

SkyrimMOD関連の個人的なメモをまとめています。

|Skyrim SE| Diziet's Auto Outfitsがうまく動作しない…しゃーない、自分で直すか!

前回の記事で導入した自動お着換えMOD「Diziet's Auto Outfits」で遊んでいたのですが、時折思った通りに動作しなかったり、クラッシュしてしまう問題が発生したのでそれに対してさらに対策を行ったのでこちらに紹介します。

前回の記事:

elepanskyrim.hatenablog.com

 

Ver0.5(experiment)系でニューゲームするとCTDしてしまう。

以下、Ver0.5expと記載。修正元MODのリンクはこちら。

skyrimspecialedition.2game.info


おま環ならぬわた環では以前この記事を書いたときはCTDしなかったのですが、その時は途中導入でした。ニューゲームで遊ぼうとすると確定CTDするようになり、本MODを抜くと起動でき、かつ.NetFrameworkのクラッシュログにもこのMODのログが出ていたので間違いないかなと思います。

ひょっとしてニューゲーム改変系MODと相性が良くないのか?あるいはMCMでの設定初期化処理のVerup処理で発生しているようなので、ほかのMCMの初期化処理の中で処理されてしまうとメモリがこんがらがってエラーを起こしてしまうとかそんななのかなんて想像しています。死ぬほど適当なこといってますハイ。

おそらく一度MODを非有効にした状態で新規ゲームを初めてセーブし、再度有効にして再起動すれば起動するのではないかと思っていますが(さらに付け加えるならほかのMODのMCM初期化処理が終了した状態のセーブであればいけるのではと)、こちらは検証してません。
というのはそれで遊べるとしても今後もニューゲームのたびにこの手間を踏むのはちょっと危険なので(忘れたらリカバリに苦労するため)、保守性を考えて泣く泣く外すことになりました。

Ver0.5(または0.4x系)で試す。

試しにVer0.5で試したところ、こちらはCTDしませんでした。やったぜ!
どうやらVer0.5expとは仕組みが違うようで、Ver0.5expは自動装備を設定したプレイヤー、NPCにそれぞれの装備用のコンテナが割り振られ、条件に応じて登録した装備をコンテナから出し入れして装備させるというものなのに対し、Ver0.5は所持品の中から保存した装備セットを装備するという形になっています。

なので例えばVer0.5で何か装備を渡したりするとバニラの防具性能重視な自動装備機能が動作して指定した装備ではなくなってしまうようでした。

セルを読み込むごとにバニラの自動装備機能が動作してしまう模様

Ver0.5では対象NPCの所持品に自動装備として指定したすべての装備が存在している必要があるため、マップ移動をするごとにバニラの自動装備処理が走ってしまい手持ちの中で最強の装備をした、つまりバラバラな装備になりますこれはSkyrimの仕様上しょうがないことですね。

これをMODの自動装備機能で上書きすることで解消する形にはなっているのですが、意図したように動かないケースも多く、装備が変わらないまままるで追いはぎ一行のようなパーティで世界を放浪していることも少なくないです。

ホットキーで手動再装備させる機能があり、そちらは常に正しく動作します。なのでおかしくなったら任意で着せ替えることでこの問題は解消できます。

が、ただマップ移動のたびにバラバラになったフォロワーの装備を手動リセットするのは些か世話が焼けます。装備を手渡しで整えるよりは楽なんですけどね…

どうにかならんか?

なんとかこの自動着替え機能をちゃんと動くようにならないだろうか?わざわざ着せてあげなくても自分で着てくれないだろうか?などと願掛けして動くようになるなら苦労しない。

MODの修正を待つべきか?しかし更新は半年以上前、更新される望みは薄いときました。

これは…

自分で修正するしかないな!

というわけでスクリプトを自分で修正することにしました。
Pupyrusスクリプトは不具合とかバグ調査しているときにネットで誰かの投稿したポストのコードを見かけた程度の知識しかなかったのですが、情熱さえあればなんとかなる!

あぁ、愛!愛!と、タロスに願掛けするとともに2023元旦の全ての時間をささげ、何とか納得のいく形で動作するように修正できたのでこちらにその修正方法を展開します。

 

Ver0.5を修正することに

Ver0.5expでないのはVer0.5expのCTD原因がどうやらSKSE関連ぽいのと、CTDの件でちらっと触れていますが発生個所がmcmの初期化処理の中で発生しているっぽくてPupyrusの知識すら危うい私には敷居が高すぎると感じたためです。

 

追記:NetScriptFrameworkクラッシュログの最初のスタックがこれ

[SP+0] 0x2353B6C1058 (char*) "Hidden Container for $empty_text"

これってもしやあれか?$empty_textに設定する文字がNULLかなんかで落ちているとか? char*は多分文字列のポインタだと思うんですが、このポインタの参照先が未初期化のままアクセスしようとして落ちてるとか…?MCMのOnVersionUpdateのガイドラインとかあるのかな。もう少し見てみますか…

 

というかC++ですよね。ドライバやOSまでかけちゃうようななんでもできる万能言語だけど同時にそれだけの知識がないと全然使いこなせない言語…C#javaみたいないろいろお膳立てしてくれる便利なフレームワークにすっかり頼ってしまっている私なんかではとてもとても…生きていくか明日からも…あ、でもこのMODはSKSEのdllプラグインMODではなくSKSEのpupyrus拡張関数を使っているMODなので、そこまで高くないのかも。

修正内容

・dz_outfits_location_effect_scriptを修正する

 ■OnLocationChangeイベントの修正

①「If !this_npc.IsInCombat() …」のif文をコメントアウトする
解説:装備を変更する条件文ですがこの判定が正しく動いていないようで、装備変更処理がコールされていないようでした。
おそらく負荷を考慮したため再装備処理させる処理を限定していると推測しますが、装備変更処理は大変軽いため※1 なくてもいいと判断しました。

コメントアウトしなくてもよさげです。というかすると、タイミングにとっては多重に着せ替え処理が走るんで負荷的にも排他制御的にも一貫性的にもよくないので、そのままにしておきましょう。

誉れ高い紳士淑女のみなさん向け:S〇xlabのアニメーションが再生されているときに状況によって着せ替え処理が走ってしまうことがあると思います。その場合は上記の条件に!Actor.HasKeyWordString("S〇xLabActive")を追加することで着替え処理を走らせないことで回避できます。アニメーション再生中はこのキーワードがアクターについているのでそれで判定する、という感じです。(〇をeに変えてね)


②dz_outfits_ability_spell.castを呼ぶ前に以下を呼ぶようにする


dz_mcm.dz_update_activate()
utility.wait(4)


解説:castメソッドをコールしても関連しているMagicEffectが全く動作しないことがあり、色々と検証を行ってみたところ、Spellを再度addして初期化することでコールされるようになりました。
dz_mcm.dz_update_activateはその各Spellのadd処理を行っている関数です。utility.wait(4)はdz_mcm.dz_update_activateの処理完了を待つのに確保した時間です。これがないとやはりcast処理が正しく動作しないことがあるようでした。ここの4という数値は現実世界の秒の単位の数字、つまり4秒ですが、最適な数値は環境によって変わるかもしれません。

 他のイベントの追加

・OnAttachedToCell
・OnCellLoad()
・OnCellDetach()

追記:・OnAttachedToCell

・OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)

※OnItemAddedは例えば他MODでスクリプトでアイテム配布をNPCに行う処理でも拾うので、アイテムを渡したときに装備が戻るのが気にならないのであれば(アイテム渡してホットキーで再装備させれば終わる話なので)ない方がいいかも。あるいは引数に持ってきている各種データから「プレイヤーから渡されたとき」という条件を特定できる何かがあればそれを条件に組み込んでうまいこと動作させられるかもしれません。


上記イベントを追記し、中身をすべて下記にします。
   
dz_mcm.dz_update_activate()
utility.wait(4)
dz_outfits_ability_spell.cast(this_npc,this_npc)

解説:OnLocationChangeイベントのみでは足らず、セル移動時や装備可能なアイテムをNPCに渡したときにやはりバニラの装備処理が走ってしまうためです。
なのでそれぞれのイベントでMODによる自動装備処理をコールする処理を追加しました。

・dz_outfits_ability_effect_scriptを修正する

■OnEffectStartイベントの修正

①処理の先頭にutility.wait(4)を追加する
解説:これはゲームロード時の初期化処理時、バニラの装備処理完了をまつための時間です。この時間も環境によって最適な時間は変わるかもしれません。

上記のような感じで、なんとか自動装備してくれるようになりました。

以上で修正は完了です。プログラマやっててよかったよ。ゲームプログラマだったらベストだったでしょうが。

 

※1Papyrus Tweaks NGを導入した環境での話です。

skyrimspecialedition.2game.info

 

課題点

  OnChangeLocationイベントが発生しないケースがある。(解決済)

これはバグではなく元からの使用で、例えばホワイトラン周辺からホワイトランの入口を使ってホワイトランの町に入ったときです。
この時OnChangeLocationイベントは発生しません。なぜならLocation情報はホワイトランのまま変わらないためです。
このように時々同じLocation情報をもったセル間を移動するケースがありその場合は自動装備処理が走らず、しかしバニラの装備処理ははしるため意図した装備をしてくれなかったりします。

追記:解決しました。OnCellDetachとOnAttachedToCellでセル移動事にイベントを拾って処理できました。

追記の追記:ロケーションもセルも変わらない高速移動(ファストトラベル)を行うとイベントが発生せずバラバラ装備になってしまう模様。ぐぬぬ

  数秒はバニラで装備したものが装備される。

  wait関数を使って待っている間NPCに設定した装備ではなくバニラの挙動に従った装備になります。なので最初の数秒はバニラで装備された不規則な装備になりますが数秒後に設定した装備になります。

──以上です。これらの問題がすべて解消できれば完璧なんですが…難しいです。痒いところに手が届くイベント関数がないんだよなぁ。エリアチェンジ時に常に発火するイベントとかそういうのが。それ以外は意図した形で自動装備処理が走ってくれます。

そういえば小耳にはさんだ話で自動装備するとエンチャントが反映されないということがあるらしいですが、見た目以外特にこだわらない適当な私はあんまりそこには興味がないので未検証です。

追記の追記の追記の追記:ふははは全部解決じゃ~!!!解決内容は一番下に書いています。

余談:わかった(?)こと

Skyrimの仕様の話なのですが、Spellのcastメソッドは対象、つまりそのcast対象であるキャラクターがロードされていないと動作しない?みたいです。
基本的にキャラクターに対してスクリプトを動作させるMODはSpellとMagicEffectを使って実装しているケースが多いと思います。このMODでもそうでした。
さて、このMODでは対象が持っているmagicEffect(dz_outfits_location_effect)がOnChangeLocationイベントを検知することで対象の装備変更をするSpellをcastするという形で装備の自動変更を実現していますが、cast時つまりlocationchange時に対象のキャラクターがロードされていないとcast先のMagicEffectスクリプトの処理がうまく動作しないようでした。Debugログも埋めこんでみましたが、cast先(dz_outfits_ability_effect)のOnEffectStartすら走っていないようで、完全に空ぶっているようでした。なのでwait関数で待っているわけです。

 

さて、これでまたポンチョを羽織ってくれます。いやーつかれた!

ソースのコンパイル環境構築はまた今度書こうと思います。



追記の追記:

あれからいろいろ観察してたんですが、セル移動時に自動装備処理が行われないのはひょっとしたらNether's Follower FrameworkのSandboxがAllowになっていると発生するのかもしれない、と思ったり。この設定だとセル移動してもフォロワーがなかなかついてこないんですよね。実際この機能を切ることでより短いwait時間でも安定してきがえるようになりました。wait(4)はフォロワーが現れるまで待っている時間で、4秒もあれば出てくるのでうまく動作するようになったということなのかもしれません。

 

追記の追記の追記:

各イベントのログを監視したところ、どういうわけか「Silver ring」がフォロワーのインベントリにゲーム開始時、またはエリア移動時にランダムに追加され、そのたびにバニラの装備処理が走っていた模様です。

おそらく何かのMODの影響だと思いますが、何れにせよわた環でこのMODを遊ぶにはOnItemAddedに細工が必要のようです。

wait関数でまつとうまいこと動作していたのは、このアイテムが追加された後に処理を走らせていたからということもあるのかもしれない…と試しにwait関数の時間を1に設定したものの、silver ringが追加されなくても着替えてくれないケースが多発。やっぱり待つ必要はあるみたいですね。

 

追記の追記の追記の追記:0.5expのCTD原因、わかりもうした

HA! FOUND YOU !! (山賊

やっぱりエリア移動時に毎回追いはぎ一行になるのはいやじゃ。ということでexp版をデバッグして検証したところ、その原因が判明し、修正することで解消することができました。

dz_outfits_mcm_menuのdz_fill_containers関数を修正する

hidden_container[i].SetDisplayName(msg+" "+pages[i])

↑関数内のこれをコメントアウトする。どうもSKSE関数のSetDisplayNameをコールして落ちているみたい。クラッシュログのあれはこの関数の引数が出ていたみたいです。引数に指定している変数の参照問題かと思いましたが、固定文字列にしても同じくCTD、同じクラッシュログが出るので関数が問題と判断しました。

このSetDisplayNameという関数はObjectReferenceの表示名を引数の内容にセットする処理をする?みたいですが、これいる?設定する目的は不明ですがなくても特に動作に支障はないようでした。

でもなんでクラッシュするんだろ。SetDisplayNameのコールするタイミングの問題かな?ニューゲーム時のMCM初期化処理でこの関数を呼ぶのがよくないのか。それとも想定外に大量のMODがインストールされていると発生するとか?eslフラグついているMODも含めて400超えているのでそういうこともあるのかも?ハイ適当なこと言ってます。

MCM初期化処理以外にロードゲーム時にもコールしているんですがそっちはCTDしないんですよね。なんでかな~なんでだろ~

メケメケメケメケ

追記:と思ったらエラーログ吐いてた。

stack:
    <unknown self>.Game.GetFormFromFile() - "<native>" Line ?
    [dz_auto_change_quest (FE0B9800)].dz_outfits_mcm_menu.dz_maintenance() - "dz_outfits_mcm_menu.psc" Line 1727
    [alias dz_player_alias on quest dz_outfits_maintenance_quest (FE0B989E)].dz_outfits_player_alias_script.OnPlayerLoadGame() - "dz_outfits_player_alias_script.psc" Line 8
[01/08/2023 - 04:11:40PM] Error: Cannot call SetDisplayName() on a None object, aborting function call
stack:

つまりSetDisplayNameはちゃんとコールできていないと。ひょっとして各種Notificationに表示される名前が正しく表示されないのはこのためか?知らんけど。"Cannot call SetDisplayName() on a None object"だから、まだコンテナのデータが読み込めてなくて発生しているとか、そんなところ?

ふむ…やっぱりおま環?まさかおま環の正体ってこういうことなのか?負荷によってデータの読み込み遅延が起きてそれによって想定通りに動作しなかったりCTDするという事象の真相は?適当なこと言ってますまたしても。

あとはVer5と同じ修正を施して、Ver5expに乗り換え!!

と、まぁそれはさておくとして起動は問題なし。あとはVer5と同じ修正を施して、Ver5expに乗り換え!!

終わりぃぃぃいい!!一瞬バニラの自動装備が走る問題も解消!!エリア移動してもばっちおーけー!ぱーぺきじゃ!!

やったああああぜええ!!!いやっほうううううう!!!

※あ、onItemEventの追加はいらないです。インベントリには必要な装備しかないはずなので。

複数人連れまわす場合、初回エリアチェンジ時に一瞬固まる?

原因は不明。まぁ最初の一回だけなんでまぁいいか…

 

追記:…まだまだ続くんじゃ↓

elepanskyrim.hatenablog.com

 

 

フリッカーもやってます★

The Elder Scrolls V  Skyrim Special Edition Screenshot 2023.01.18 - 21.42.00.61