スナイパーライフルでスコープを覗きながら射撃を行い、即座に覗くのをやめると、勝手に全弾リロードしてしまいます。これをなんとかしました。
リロードはアニメーション
Actorの動作はIdle Animationとして定義されており、リロードはRaiderReloadのようでした。なので条件の設定次第ではアニメーションを再生できなくなります。
ためしに「プレイヤー以外」という条件を追加したところ、リロードできなくなりました。リロードアニメーションのhkxファイルの中でリロード動作の完了時に弾薬を補充するという命令が走るのではないかと思われます。
グローバル変数IsIronSightedを用意して、プレイヤーがスコープを覗いているときに1、そうでないときに0になるようにして「プレイヤー以外」もしくは「IsIronSightedが0である」という条件を追加しました。
プレイヤー以外という条件がないと、プレイヤーがスコープを覗いている間はNPCがリロードできなくなります。
アニメーションのイベントを補足してグローバル変数を操作
サンプルコードです。
勝手にリロードするのをやめさせるサンプル (papyrus)
ScriptName SampleQuest extends Quest
; あらかじめグローバル変数を用意して紐付けしておく
GlobalVariable Property IsIronSighted Auto
Keyword Property WeaponTypeSniper Auto
Actor Property PlayerRef Auto
int TimerCheckWeapon = 1 Const
int TimerSighted = 2 Const
; プレイヤーの装備の変更に関するイベントの捕捉をはじめる
Function RegisterEvents()
RegisterForRemoteEvent(PlayerRef, "OnItemEquipped")
RegisterForRemoteEvent(PlayerRef, "OnItemUnequipped")
EndFunction
; プレイヤーのスコープに関するイベントの捕捉をはじめる
Function RegisterForSightEvents()
RegisterForAnimationEvent(PlayerRef, "sightedStateEnter")
RegisterForAnimationEvent(PlayerRef, "sightedStateExit")
EndFunction
; プレイヤーのスコープに関するイベントの捕捉をやめる
Function UnregisterForSightEvents()
UnregisterForAnimationEvent(PlayerRef, "sightedStateEnter")
UnregisterForAnimationEvent(PlayerRef, "sightedStateExit")
EndFunction
; クエストが稼働すると呼ばれる
Event OnQuestInit()
RegisterForRemoteEvent(PlayerRef, "OnPlayerLoadGame")
RegisterEvents()
EndEvent
; アニメーションが発生すると呼ばれる
Event OnAnimationEvent(ObjectReference akSource, string asEventName)
if asEventName == "sightedStateEnter"
; タイマーの予約をキャンセル
CancelTimer(TimerSighted)
; 覗くのを開始したので1を設定
IsIronSighted.SetValueInt(1)
else
; 覗くのをやめたのでタイマーを予約
StartTimer(0.5, TimerSighted)
endif
EndEvent
; タイマーの時間がくると呼ばれる
Event OnTimer(Int aiTimerID)
if aiTimerID == TimerCheckWeapon
Form kWeapon = PlayerRef.GetEquippedWeapon(0)
if kWeapon && kWeapon.HasKeyword(WeaponTypeSniper)
RegisterForSightEvents()
else
UnregisterForSightEvents()
endif
elseif aiTimerID == TimerSighted
; 覗くのをやめてから0.5秒が経過したので0を設定
IsIronSighted.SetValueInt(0)
endif
EndEvent
; ゲームがロードされると呼ばれる
Event Actor.OnPlayerLoadGame(Actor akActorRef)
RegisterEvents()
EndEvent
; プレイヤーが装備をすると呼ばれる
Event Actor.OnItemEquipped(Actor akSender, Form akBaseObject, ObjectReference akReference)
if akBaseObject as Weapon
CancelTimer(TimerCheckWeapon)
StartTimer(0.5, TimerCheckWeapon)
endif
EndEvent
; プレイヤーが装備を外すと呼ばれる
Event Actor.OnItemUnequipped(Actor akSender, Form akBaseObject, ObjectReference akReference)
if akBaseObject as Weapon
CancelTimer(TimerCheckWeapon)
StartTimer(0.5, TimerCheckWeapon)
endif
EndEvent
理由は不明ですが、イベントが連続して発火するので、スコープの停止の判定はTimerで制御しています。
sightedStateEnterイベントは狙いをつけると発火するのですが、スコープを覗いているかどうかに関係なく発火してしまいます。スコープを覗いているときだけを検出する方法がわからないので、武器のキーワードを見ています。