攻撃を受けたら服が破れて壊れていくようにしてみました。
作るべき3つのポイント
- 破れた装備のメッシュ
- アタッチメントを実現するレコード一式
- 部位破壊を実現するスクリプト
破れた装備のメッシュを作る
Outfit Studioで装備のプロジェクトを読み込みます。
破れるShapeを決めて選択したら、残す部分にマスクをかけます。Tを押してテクスチャは隠したほうがいいです。
メニューからSlider、New Zap Sliderを選んでZap Sliderを作成します。
プロジェクトを別名で保存します。生成されるメッシュの名前を別のものにします。
Display Nameを変更したらTo Projectボタンをクリックすると下にも反映されるので楽です。
Output File Nameも忘れずに変更します。
あとはBodySlideでメッシュをBuildするだけです。
上の方にZap Sliderが出てきますので、チェックを入れます。
アタッチメントを実現するレコードを作る
何でもいいですが、プラグインを1つ用意します。今回はMyBreakArmors.espとしました。
必要なものは以下の通りです。
- Armor
- Armor Addon
- Constructible Object
- Keyword
- Object Modification
- Component
Armorは防具そのものです。Armor Addonは防具の見た目を決めます。Constructible Objectは装備を作成するレシピです。ここまではSkyrimと同じです。
Fallout 4はアタッチメントという仕組みが用意されています。防具に機能を追加したり、見た目をかえたりできます。
Keywordがアタッチメントのスロットになり、防具とアタッチメントを紐付ける目印になります。
Object Modificationはアタッチメントそのものです。アタッチメントを取り付けるとどうなるのかを定義します。
Componentは材料です。アタッチメントを取り付けるときに要求される材料になります。
Constructible Objectで防具にアタッチメントを取り付けるレシピが必要です。これがないとアーマー作業台で出てこないので取り付けができません。
綺麗、傷んでいる、ボロボロの3段階を用意することにします。
Keyword
Mod AssociationとAttach Pointをそれぞれ用意します。
KeywordのTypeのところで指定です。Editor IDはバニラにならってma_とap_にしました。
Armor
ModelsでArmor Addonを指定しますが、このようにバリエーションを用意して指定します。
Addon Indexが重要です。きちんと1から並べます。
KeywordsにMod Association (ma_)のキーワードを指定します。
Attach Parent SlotsにAttach Point (ap_)のキーワードを指定します。
Object Modification
アタッチメント3つ分用意します。
内容はAddonIndexの変更になります。Value 1のところにAddon Indexの値を入れます。Armorで設定したものと合わせます。
Component
ダミーのComponentを用意します。
Constructible Object
アタッチメントのレシピを3つ用意します。
「綺麗な状態だ」にするレシピは、いわゆる修理に相当しますので、バニラの布などを指定します。
Created ObjectのところはObject Modificationを指定することになります。
「傷んでいる」と「ボロボロだ」にするレシピは意図的に取り付けできないようにするため、さきほど用意したComponentを指定します。
Armor
Armorに戻り、Object TemplateのところでデフォルトのObject Modificationを指定します。
部位破壊を実現するスクリプト
サンプルソースコードを用意しました。
Questを1つ作って紐付けます。
BreakArmorsScript.psc (papyrus)
Scriptname MyBreakArmors:BreakArmorsScript Extends Quest
Actor Property PlayerRef Const Auto Mandatory
Keyword Property BreakableArmor Const Auto Mandatory
BreakArmorSyntax[] Property BreakArmorsList Const Auto Mandatory
Struct BreakArmorSyntax
Int BipedSlotIndex
Form BaseArmor
ObjectMod ObjectMod01
ObjectMod ObjectMod02
ObjectMod ObjectMod03
EndStruct
Function Initialize()
RegisterForRemoteEvent(PlayerRef, "OnPlayerLoadGame")
EndFunction
Function Initialize2()
RegisterForHitEvent(PlayerRef, aiBlockFilter = 0)
EndFunction
Function ShowBreakArmorsList()
Int i = BreakArmorsList.Length
Debug.Trace("BreakArmorsList " + i)
while i > 0
i -= 1
Debug.Trace(i)
Debug.Trace("BipedSlotIndex = " + BreakArmorsList[i].BipedSlotIndex)
Debug.Trace("BaseArmor = " + BreakArmorsList[i].BaseArmor)
Debug.Trace("ObjectMod01 = " + BreakArmorsList[i].ObjectMod01)
Debug.Trace("ObjectMod02 = " + BreakArmorsList[i].ObjectMod02)
Debug.Trace("ObjectMod03 = " + BreakArmorsList[i].ObjectMod03)
endwhile
EndFunction
Function Debug()
Actor:WornItem kWornItem = PlayerRef.GetWornItem(3)
Debug.Trace("kWornItem.Item = " + kWornItem.Item)
ObjectMod[] kObjectMods = PlayerRef.GetWornItemMods(3)
Int i = kObjectMods.Length
while i > 0
i -= 1
Debug.Trace("kObjectMods " + i)
ObjectMod:PropertyModifier[] kProperties = kObjectMods[i].GetPropertyModifiers()
Int j = kProperties.Length
while j > 0
j -= 1
Debug.Trace("kProperties " + j)
Debug.Trace("Target = " + kProperties[j].Target)
Debug.Trace("Operator = " + kProperties[j].Operator)
Debug.Trace("Object = " + kProperties[j].Object)
Debug.Trace("Value1 = " + kProperties[j].Value1)
Debug.Trace("Value2 = " + kProperties[j].Value2)
endwhile
endwhile
EndFunction
Function TryToBreakArmor()
Actor:WornItem kWornItem
Int[] iBipedSlotList = new Int[2]
iBipedSlotList[0] = 3
iBipedSlotList[1] = 10
Int i = Utility.RandomInt(0, 1)
Int j = iBipedSlotList.Length
while j > 0
j -= 1
kWornItem = PlayerRef.GetWornItem(iBipedSlotList[i])
if kWornItem && kWornItem.Item.HasKeyword(BreakableArmor)
if TryToThisArmor(kWornItem.Item, iBipedSlotList[i]) == 1
return
endif
endif
i += 1
if i >= iBipedSlotList.Length
i = 0
endif
endwhile
EndFunction
Int Function TryToThisArmor(Form akArmor, Int aiBipedSlotIndex)
ObjectMod[] kObjectMods = PlayerRef.GetWornItemMods(aiBipedSlotIndex)
Int i = kObjectMods.Length
if i == 0
; this armor is not breakable
Debug.Trace("TryToThisArmor " + akArmor + " this armor is not breakable")
return 0
endif
Int iListIndex = BreakArmorsList.FindStruct("BaseArmor", akArmor, 0)
while i > 0
i -= 1
ObjectMod:PropertyModifier[] kProperties = kObjectMods[i].GetPropertyModifiers()
Int k = kProperties.FindStruct("Target", 263, 0)
if k != -1
Float v = kProperties[k].Value1
if v == 1.0
if BreakArmorsList[iListIndex].ObjectMod02
PlayerRef.AttachModToInventoryItem(akArmor, BreakArmorsList[iListIndex].ObjectMod02)
; success
Debug.Trace("TryToThisArmor " + akArmor + " success")
return 1
else
; no break omod
Debug.Trace("TryToThisArmor " + akArmor + " no break omod")
return 2
endif
elseif v == 2.0
if BreakArmorsList[iListIndex].ObjectMod03
PlayerRef.AttachModToInventoryItem(akArmor, BreakArmorsList[iListIndex].ObjectMod03)
; success
Debug.Trace("TryToThisArmor " + akArmor + " success")
return 1
else
; almost broken
Debug.Trace("TryToThisArmor " + akArmor + " almost broken")
return 2
endif
else
; almost broken
Debug.Trace("TryToThisArmor " + akArmor + " almost broken")
return 2
endif
endif
endwhile
; this armor is not breakable
Debug.Trace("TryToThisArmor " + akArmor + " this armor is not breakable")
return 0
EndFunction
Event OnHit(ObjectReference akTarget, ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, \
bool abSneakAttack, bool abBashAttack, bool abHitBlocked, string apMaterial)
;Debug.Trace("akAggressor=" + akAggressor + " akSource=" + akSource)
Utility.Wait(5.0)
if akSource as Weapon
if !(akTarget as Actor).IsDead() && !(akTarget as Actor).IsInKillMove()
;if Utility.RandomInt(1, 5) == 1
TryToBreakArmor()
;endif
endif
endif
RegisterForHitEvent(PlayerRef, aiBlockFilter = 0)
EndEvent
Event OnQuestInit()
Initialize()
Initialize2()
EndEvent
Event Actor.OnPlayerLoadGame(Actor akActorRef)
Initialize2()
EndEvent
補足
戦闘中はできるだけ処理を早くしたいので、まず部位破壊に対応している防具であることを示すキーワード「BreakableArmor」を用意してArmorに付けておきます。これでキーワード検索を行えば条件判断をほぼゲームエンジン任せにできるので、スクリプトの稼働時間が短くなるはずです。
プレイヤーがキルムーブを受けている最中に防具のアタッチメントを操作するとCTDするような印象がありました。そこでOnHitを受けてから少し待ってからキルムーブも死んでもいないことを確認した上で処理を行うようにしました。
BreakArmorSyntaxという構造体(Struct)ですが、以下のようになっています。
キー | 内容 |
---|---|
BipedSlotIndex | スロット番号。GetWornItemやGetWornItemModsで使用する。 |
BaseArmor | ArmorのFormを指定。 |
ObjectMod01 | Object Modificationその1。「綺麗な状態」のものを指定。 |
ObjectMod02 | Object Modificationその2。「傷んでいる」のものを指定。 |
ObjectMod03 | Object Modificationその3。「ボロボロだ」のものを指定。 |
BipedSlotIndexは胴装備が3番です。スカートも用意して10番も指定しました。