Papyrusでコードを書く時、真っ先に参考にするのがCK Wikiだと思います。
ところが、ここに書かれていない関数が実はあります。例えばここにSKSEのUtility.pscというファイルがあります。このソースの下のほうにあるCreate*Array関数、Resize*Array関数です。
Papyrusの配列は要素数を定数でしか決められないので、要素数は決め打ち、ゲーム内で動的に決めることもサイズを変更することもできません。Create*Array関数なら動的に要素数を決められますし、Resize*Array関数を使えば一度作成した配列を小さくしたりできます。
アクターの装備している防具の一覧を取得する処理を考えてみます。CK Wikiのサンプルソースコードを参考に関数を作ってみます。
337 (papyrus)
Form[] Function GetWornArmors(Actor akTarget)
Form[] kArmors = new Form[32]
Int iIndex
Int iSlotsChecked
Int iThisSlot = 0x01
iSlotsChecked += 0x00100000 ; チェックを省くスロット
iSlotsChecked += 0x00200000
iSlotsChecked += 0x80000000
while iThisSlot < 0x80000000
if Math.LogicalAnd(iSlotsChecked, iThisSlot) != iThisSlot
Form kThisArmor = akTarget.GetWornForm(iThisSlot)
if kThisArmor
kArmors[iIndex] = kThisArmor
iIndex += 1
iSlotsChecked += (kThisArmor as Armor).GetSlotMask()
else
iSlotsChecked += iThisSlot
endif
endif
iThisSlot *= 2
endWhile
return kArmors
EndFunction
この関数はActorが装備している防具の一覧をFormの配列で返します。要素数は32個固定で、先頭の0から順に防具がFormの形で格納されています。
340 (papyrus)
Form[] kArmors = GetWornArmors(Game.GetPlayer()) ; プレイヤーの装備一覧を取得する
Int iCount = kArmors.Length ; 常に32を返す
さて、防具の一覧からランダムでひとつ選び、何かしようとしたとき、きれいに処理する方法がありません。ループを最大32回もまわしていく必要があります。
342 (papyrus)
Form[] kArmors = GetWornArmors(Game.GetPlayer())
Int iIndex = Utility.RandomInt(0, 31) ; ループの開始位置をランダムに決定
Int iMaxIndex = kArmors.Length ; ループの最大回数は配列の個数
Int iCount = 0
while iCount = iMaxIndex ; 配列の最後に達したら先頭に戻す
iIndex = 0
endif
endif
endWhile
結構無駄がありますね。配列の中身がどうなっているかは、実際に覗いてみないとわからないので、全部見ていく必要があるのです。もしこの処理が、その場にいるアクター全員が攻撃を受けるたびに動いていたら、相当の無駄が生じることになります。そこで、Resize*Array関数を使って改良してみます。
344 (papyrus)
Form[] Function GetWornArmors(Actor akTarget)
Form[] kArmors = new Form[32]
Int iIndex
Int iSlotsChecked
Int iThisSlot = 0x01
iSlotsChecked += 0x00100000 ; チェックを省くスロット
iSlotsChecked += 0x00200000
iSlotsChecked += 0x80000000
while iThisSlot < 0x80000000
if Math.LogicalAnd(iSlotsChecked, iThisSlot) != iThisSlot
Form kThisArmor = akTarget.GetWornForm(iThisSlot)
if kThisArmor
kArmors[iIndex] = kThisArmor
iIndex += 1
iSlotsChecked += (kThisArmor as Armor).GetSlotMask()
else
iSlotsChecked += iThisSlot
endif
endif
iThisSlot *= 2
endWhile
return Utility.ResizeFormArray(kArmors, iIndex)
EndFunction
最後にResizeFormArrayで配列の空いている部分を削除しました。これで配列の要素数は見つかった防具の数になります。
346 (papyrus)
Form[] kArmors = GetWornArmors(Game.GetPlayer()) ; プレイヤーの装備一覧を取得する
Int iCount = kArmors.Length ; 見つかった防具の数を返す
では、ランダムに何かしてみます。
348 (papyrus)
Form[] kArmors = GetWornArmors(Game.GetPlayer())
if kArmors.Length > 0
Int iIndex = Utility.RandomInt(0, kArmors.Length - 1)
Debug.Trace("今回の防具はこれ: " + kArmors[iIndex])
endif
とても短くなりました。配列の要素数が実際に見つかった防具の数なので、要素数が0なら何も見つからなかった、ということを簡単に判別できます。また、配列に無駄な要素がないということは、ランダムに抜き出す処理も乱数を1個作るだけで済んでしまうのです。