設計
対象を拘束する操作を何らかの形で実装する必要があります。
思いつく操作はいくつかあります。拘束するのに会話から行うのは少し違うような気がしますし、戦闘中に会話している暇はありません。ホットキーはコントローラーはおろか、キーボードにすら空きがないため、これ以上増やしたくないです。魔法(パワー)も切り替えが面倒です。
単純に、逃げ出したActorをアクティベートしたら拘束されるようにしました。
拘束のポーズはバニラに用意されているので、それをそのまま使います。暗殺者ギルドのクエストの最初の方で出てくるやつです。
アクティベートを捕捉
Perkを作ってプレイヤーに付与します。
Entry Pointは2つ用意します。
拘束用のEntry Point
こんな感じで作ります。
条件はしっかり付けないと、予期せぬ誤動作の原因となります。
IsFleeingが1なら怯えて逃げ出しているという条件です。
戦闘の対象がプレイヤーである、という条件も入れてありますが、NPC同士の戦闘に介入したいのなら取ってもいいでしょう。
拘束中のEntry Point
拘束したらルートしたいですし、解放する処理も必要です。
拘束はスペルを持たせることにしたので、それを条件に加えます。
Papyrus Fragment
Perkにスクリプトを付けます。それぞれのEntry Pointでスクリプトが実行されるようにします。
拘束用Perkのスクリプト (papyrus)
;BEGIN FRAGMENT CODE - Do not edit anything between this and the end comment
;NEXT FRAGMENT INDEX 7
Scriptname eqPerkActivationScript Extends Perk Hidden
;BEGIN FRAGMENT Fragment_5
Function Fragment_5(ObjectReference akTargetRef, Actor akActor)
;BEGIN CODE
; Activate Living Actor (Hostile, Fleeing)
Int result = eqQuestMain.ShowMessage(eqMsgDoYouWantToRestrain, akTargetRef, aiLevel = 0)
if result == 0
(akTargetRef as Actor).AddSpell(eqAbilityRestrained)
endif
;END CODE
EndFunction
;END FRAGMENT
;BEGIN FRAGMENT Fragment_6
Function Fragment_6(ObjectReference akTargetRef, Actor akActor)
;BEGIN CODE
; Activate Living Actor (Restrained)
Int result = eqQuestMain.ShowMessage(eqMsgWhatDoYouWantToDo, akTargetRef, aiLevel = 0)
if result == 0
Int h = ModEvent.Create("emOpenContainer")
if h
ModEvent.PushForm(h, akTargetRef)
ModEvent.Send(h)
endif
Utility.Wait(0.5)
String cmd = "\"" + PO3_SKSEFunctions.IntToString(akTargetRef.GetFormID(), true) + "\".openactorcontainer 1"
ConsoleUtil.ExecuteCommand(cmd)
elseif result == 1
(akTargetRef as Actor).RemoveSpell(eqAbilityRestrained)
endif
;END CODE
EndFunction
;END FRAGMENT
;END FRAGMENT CODE - Do not edit anything between this and the begin comment
Message Property eqMsgDoYouWantToRestrain Auto
Message Property eqMsgWhatDoYouWantToDo Auto
Spell Property eqAbilityRestrained Auto
自前のフォロワー拡張Modに作ったのでFragmentのインデックスが中途半端ですが、作るPerkのインデックスに合わせます。
メッセージですが、「拘束しますか?」と「何をしますか?」を作って紐づけます。
それから拘束を実現するアビリティも用意して紐づけます。
拘束中の対象のインベントリを開く方法がわからなかったので、コンソールコマンドのOpenActorContainerを実行しています。そのため、PowerOfThree Papyrus ExtenderとConsoleUtilが必要になります。
拘束するアビリティ
常時発動型のSpellとMagic Effectを用意します。
Magic Effectに付けるスクリプトは以下になります。
eqEffectRestrainedScript (papyrus)
Scriptname eqEffectRestrainedScript Extends ActiveMagicEffect
Event OnEffectStart(Actor akTarget, Actor akCaster)
akTarget.SetRestrained(true)
;akTarget.SetActorValue("Aggression", 0)
akTarget.StopCombat()
akTarget.SheatheWeapon()
Utility.Wait(1.0)
Weapon kWeapon = akTarget.GetEquippedWeapon(abLeftHand = false)
if kWeapon
akTarget.UnequipItemEx(kWeapon, equipSlot = 1) ; Right Hand Slot
endif
kWeapon = akTarget.GetEquippedWeapon(abLeftHand = true)
if kWeapon
akTarget.UnequipItemEx(kWeapon, equipSlot = 2) ; Left Hand Slot
endif
Debug.SendAnimationEvent(akTarget, "IdleBoundKneesStart")
EndEvent
Event OnEffectFinish(Actor akTarget, Actor akCaster)
if akTarget.Is3DLoaded()
Debug.SendAnimationEvent(akTarget, "IdleChairExitStart")
endif
akTarget.SetRestrained(false)
EndEvent
Event OnCellAttach()
GetTargetActor().SetRestrained(false)
EndEvent
Magic EffectはArchtypeをCalmに、Actor ValueをAggresionにします。これでこのアビリティを付与している最中はAggressionが低下するので戦闘から離脱する状態になってくれます。
拘束はSetRestrainedで移動を禁止したあとに拘束用アニメーションを再生させています。
アクティベート時のメッセージ
対象の名前をメッセージに含めるために、わざわざクエストを用意して対象をReference Aliasに入れるという面倒なことをしています。省いてメッセージも簡略化すれば楽になります。
拘束用
拘束中の操作用
こちらは選択肢がルート、解放、キャンセルの順です。Perkのスクリプトと関連しているので順番に注意します。
不具合について
拘束中にゲームをセーブしてロードすると、アニメーションが再現されないことがあるようです。これは面倒なので放置しています。
拘束中の対象がセルにAttachされたタイミングで拘束を解いています。これは、色々ある面倒な処理にたいする対策です。
拘束したままエリア移動すると、現在アクティブなセルが切り替わる都合上、対象はセルからDetachされます。そこで魔法が切れる場合と切れない場合があります。切れるのはセルがメモリにない場合、切れないのはセルがまだメモリに残っている場合です。プレイヤーが移動した直後は、さっきまでいたセルはメモリに残っているので、魔法は切れません。この状態でゲームをセーブしてSkyrimを再起動すると、OnEffectFinishは発火しないが魔法は切れているという状態になります。
かなりややこしいので、魔法は切れていないがセルを移動して付いてきた場合に、SetRestrainedをオフにして拘束を解いています。必ず付いてくるというわけでもないようなので、付いてこなかったときはそのままですが、アビリディは付与されたままなので、後日再会するようなことがあれば、その時はまたOnEffectStartが発火するので拘束された状態になります。