おおまかな実装はこのようになります。
- プレイヤーのブロックの開始と終了をアニメーションイベントを使って捕捉します。
- Timed Blockが有効かどうかをグローバル変数に設定します。
- Perkで被ダメージやStagger効果を変更します。
イベントの捕捉
QuestとReferenceAliasを作ります。ReferenceAliasにPlayerを登録します。スクリプトを用意してReferenceAliasに割り当てます。
スクリプトでは以下のことをします。
- RegisterForAnimationEvent関数を使って捕捉するアニメーションイベントを登録します。イベント名はブロックの開始がblockStartOutで、ブロックの終了がblockStopです。
- OnAnimationEvent関数でイベントを捕捉した時の処理を書きます。
- ブロックの開始では、グローバル変数aaaTimedBlockに1を設定して、Timed Blockが有効であることを明示します。Timed Blockの時間切れを処理するため、RegisterForSingleUpdateでOnUpdateイベントを登録します。
- ブロックの終了では、グローバル変数aaaTimedBlockに0を設定して、Timed Blockが無効であることを明示します。
- OnUpdateイベントで時間切れを捕捉した時の処理を書きます。ブロックの終了の時の処理と同じでいいです。
これで、最低限のTimed Block判定を実装できました。攻撃してきた対象に何かさせたいので、そのためのイベントも処理します。
- OnHit関数で攻撃を受けた時の処理を書きます。
- まず、引数のabHitBlockedがfalseの時はブロックに失敗しているので、即座に関数を抜けます。
- グローバル変数aaaTimedBlockが0の時はTimed Blockが無効なので、やはり関数を抜けます。(これは結構無駄なので、スクリプトで変数を定義してアニメーションイベントと共有する方が効率がいいです)
- 引数のakAggressorが攻撃者なので、akAggressor as Actorでキャストすれば攻撃者がActorかどうか判定できます。Actorでない場合は関数を抜けます。(罠などから攻撃を受けた場合は、攻撃者が人ではなく物になります)
- プレイヤーと攻撃者との位置関係、つまり角度を調べます。GetHeadingAngle関数を使うと手っ取り早いです。必要なら角度に応じて処理をわけます。
- 攻撃者にDebug.SendAnimationEventを使ってstaggerさせたり、デバフの魔法をかけたりと、お好きなようにします。
バニラのOnHitイベントのかわりにPO3 Papyrus ExtenderのOnHitExイベントを使うようにするとスクリプト効率を大幅に改善できます。
被ダメを軽減する
Timed Blockに成功したら被ダメージを減らしたいです。これはPerkを使って実装します。
被ダメージの操作はPerkのEntry PointからMod Incoming Damageを選びます。
まず、Perkの発動条件に「グローバル変数aaaTimedBlockが1の時」を設定します。
さらに、Attackerとプレイヤーの角度を調べます。GetHeadingAngleまたはGetRelativeAngleを使って正面からの攻撃かどうかをみます。これを設定しないと背後からの攻撃でも被ダメージが減ってしまいます。
GetHeadingAngleが単純に相手の位置をみる方法、GetRelativeAngleは相手の向きも考慮にいれた方法です。GetHeadingAngleだとConditionで設定するのが難しいようなので、GetRelativeAngleを使い150から210の間を条件として設定しました。
この150と210という数値は決め打ちになってしまいますが、ここにグローバル変数を使う方法もあります。Conditionを設定する画面でUse Globalにチェックを入れるとグローバル変数を指定できるようになります。MCMで角度を変更したらグローバル変数に反映するようにすれば、Perkの動作が動的に変化して角度判定がより正確になります。
Staggerを無効にしたいので、Entry PointにMod Incoming Staggerを選んだものも用意します。発動条件は同じです。
エフェクトの作成
参考サイト:NifSkopeでのエフェクトアニメーション調整
まずnifファイルの作成からです。
- Skyrim – Meshes1.bsaからMeshes\Magic\ShieldSpellBodyFX.nifとMeshes\Magic\CommandTargetFX.nifを取り出します。
- NifScopeで両者を開きます。
- ShieldSpellBodyFX.nifにはシーケンスが3つあります。mBegin、mLoop、mEndの3つです。使いたいのはmEndだけです。
- このファイルだけをいじっていてもmEndのみを再生させることはどうやらできないみたいです。
- CommandTargetFX.nifを見ると、シーケンスが1つしかなく、mIdleがあるのみです。
- 再生するシーケンスの順番や時間を決めているのはhkxファイルのようです。
- ShieldSpellBodyFX.nifはMagic\ShieldSpellBodyFX.hkx、CommandTargetFX.nifはMagic\IdleOnLoad.hkxを使っていました。
- ShieldSpellBodyFX.nifのhkxファイルの指定をIdleOnLoad.hkxに変更して、mEndノードの名前をmIdleに変更します。
- これでShieldSpellBodyFX.nifのmEndシーケンスだけを再生できました。名前をTimedBlockFX.nifとして保存します。
これを再生させるためのレコードをCKで作ります。
- MiscellaneousのArt ObjectにあるShieldSpellBodyFXを複製してaaaTimedBlockFXとし、nifの指定をTimedBlockFX.nifにします。
- Magic Effectを用意してHit Effect ArtのところでaaaTimedBlockFXを指定します。
- このMagic Effectが発動するとTimedBlockFX.nifのエフェクトが再生されます。