戦闘関連のModを作るうえで調べたことをまとめます。
攻撃の出かかりを検知
スクリプトの場合はイベントを捕捉するのがよさそうです。
攻撃の最初にPowerAttack_Start_Endが発生します。パワーアタックだけでなくノーマルアタックでも発生します。これでいいように思いますが、ノーマルアタックを連続で出そうとすると、2回目以降はPowerAttack_Start_Endが出ないので使えません。
発生がわずかに遅れますが、preHitFrameなら必ず発生するようです。ただし、パワーアタックで2回発生したりするので、扱いに注意です。
CKでCondition Functionを使う場合は、GetAttackStateがいいようです。
パワーアタックの出かかりを検知
意外に難しいです。
Animation Eventsを使う
Animation EventsにPowerAttack_Start_Endというイベントがあるので、これで良さそうに思えますが、これはノーマルアタックでも発生します。また、状況次第では発生しません。
そこで、かならず発生するpreHitFrameイベントを見て、さらにAnimationVariableでiStateの値を見れはいいみたいです。(参考)
パワーアタック検知(Animation Events) (papyrus)
Scriptname SampleScript Extends ActiveMagicEffect
Actor MySelf
Event OnEffectStart(Actor akTarget, Actor akCaster)
MySelf = akTarget
RegisterForAnimationEvent(MySelf, "preHitFrame")
EndEvent
Event OnAnimationEvent(ObjectReference akSource, string asEventName)
if MySelf.GetAnimationVariableInt("iState") == 14
; パワーアタック
endif
EndEvent
人型NPCの場合は問題ないみたいです。人型でないとか、あとプレイヤーは使えないようです。
slowdownStartイベントを使う手があります。これはパワーアタックでしか発生しないようです。ただ、ニュートラルや前方のパワーアタックは発生しますが、後退や左右のパワーアタックでは発生しないみたいです。Modで追加された独自アニメーションを持つ武器もダメでした。
Condition Functionを使う
Magic EffectのCondition Functionに「対象がパワーアタック中か」というのがあるので、preHitFrameの時にスペルをかけて判定してもよさそうです。
パワーアタック検知(Condition Function) (papyrus)
Scriptname SampleScript Extends ActiveMagicEffect
Spell Property aaaSpellPowerAttackFX Auto
Actor MySelf
Bool PlayedPowerAttackFX
Event OnEffectStart(Actor akTarget, Actor akCaster)
MySelf = akTarget
RegisterForAnimationEvent(MySelf, "preHitFrame")
RegisterForAnimationEvent(MySelf, "HitFrame")
EndEvent
Event OnAnimationEvent(ObjectReference akSource, string asEventName)
if asEventName == "HitFrame"
PlayedPowerAttackFX = false
else
if !PlayedPowerAttackFX
aaaSpellPowerAttackFX.Cast(MySelf)
PlayedPowerAttackFX = true
endif
endif
EndEvent
プレイヤーでも発動しましたが、武器によってはConditionが真にならないようです。片手メイスはダメで、Modで追加された両手槍はOKだったり、安定しません。
あと、preHitFrameを監視し、さらにスペルでCondition Functionを使ってチェックなので、処理がだいぶ遅いように思います。これからパワーアタックが出るというタイミングではなく、出ましたというタイミングで処理されてしまいます。
PowerAttack_Start_Endを使うと、この段階ではパワーアタックかどうかがまだ確定していないらしく、成功しませんでした。
出かかりではなくなりますが、あらかじめスペルのConditionに「パワーアタック中である」という条件を設定したものをActorに持たせておきます。パワーアタックを発動すれば自動的にMagic Effectが有効になるので、これでも検知できます。おそらくスペルの条件判定は毎秒なのでタイミングが不定となりますが、早ければ予備動作に入った瞬間に検知できることもあるようです。
キー入力を監視する
完全にプレイヤー限定となりますが、キー入力を監視して、パワーアタックを判定する方法もあります。
パワーアタック検知(キー入力監視) (papyrus)
Scriptname SampleScript Extends ReferenceAlias
Int AttackKey
Bool IsKeyPressed
Event OnInit()
AttackKey = Input.GetMappedKey("Right Attack/Block", deviceType = 0xFF)
RegisterForKey(AttackKey)
RegisterForAnimationEvent(Game.GetPlayer(), "preHitFrame")
EndEvent
Event OnAnimationEvent(ObjectReference akSource, string asEventName)
if asEventName == "preHitFrame"
if IsKeyPressed
; パワーアタック
endif
endif
EndEvent
Event OnKeyDown(Int aiKeyCode)
IsKeyPressed = true
EndEvent
Event OnKeyUp(Int aiKeyCode, Float afHoldTime)
IsKeyPressed = false
EndEvent
Input.IsKeyPressedという関数があるので、こちらのほうがシンプルに書けそうなのですが、なぜか常にfalseを返すのでダメでした。
あくまでも入力をみているだけですので、スタミナ切れでパワーアタックが出なかったとしても出たとみなされてしまうのが問題です。
Perkを使う
これは悪手です。Apply Combat Hit Spellを使えば、特定の条件で攻撃がヒットした時に相手にスペルを付与できますが、このApply Combat Hit SpellというEntry PointはActor 1体につき同時に1つしか発動できないという制約があるようで、安易にPerkを持たせると他のPerkに影響が出てしまうためです。
攻撃を発動させる
SendAnimationEventで大丈夫でした。ちゃんと当たり判定も出ます。逆にPlayIdleを使うと殴ってくれませんでした。
攻撃させる (papyrus)
Debug.SendAnimationEvent(MySelf, "blockStop")
MySelf.SetAnimationVariableBool("iWantBlock", false)
Debug.SendAnimationEvent(MySelf, "attackStart") ; 出る
;MySelf.PlayIdle(attackRightRoot) ; 出ない
NPCに攻撃させるには、Combat Styleでの大雑把な設定だとAIまかせになってしまうので、今このタイミングで攻撃してほしいという時に攻撃してくれるとは限りません。PackageのProcedureで強引に攻撃させる方法もありますが、こちらは主にお芝居用なので戦闘Modで使うには向いていません。