Nested Arrays with Papyrus

From Nexus Mods Wiki
Jump to: navigation, search

What is a "Nested Array"?

Before we talk about nested arrays, you will need a basic understand of what arrays are and how they work. The best information on this subject can be found in the Creation Kit wiki.

With an array being a list of items stored in a single properly, a nest array is an array of arrays. Which means each entry in the top level array is also an array.

Here is an example of a normal array:

[0] Item 1
[1] Item 2
[2] Item 3

This is an example of a nested array:

[0] Item 1
[1] Item 2 (array)
    [0] Nested Item 1
    [1] Nested Item 2
[2] Item 3

As you can see, item 2 has multiple values, stored in an array.

Unfortunately, Papyrus does not natively support nested arrays, so we'll need to build our own.

Why would I use a nested array?

While there are many possible applications, one use case would be to store several related forms in a single value, saving on adding multiple properties. Personally, I used to the create a dynamic MCM menu for Legacy of the Dragonborn that will store which formlists it is reading for each MCM page. We won't be going that deep in this article though as this is just demonstrating how to use it.

Creating a nested array

First, and most importantly, you need to establish why you want to use a nested array because the design of your functions will depend on it. Our use case in this example is simply to prove the concept is possible.

This method also heavily relies on the Script Extender, so ensure you have it install and the source files in place.

We'll start out with a basic script featuring two arrays:

Scriptname ArrayProofofConcept extends Quest

Weapon[] Property wArray Auto
{A simple array you should fill with a few weapons (3 is a decent number).}

Actorbase[] Property abArray Auto
{Another simple array, this time of Actor bases. Fill 3 or more random values.}

String[] Property sArray Auto Hidden
{We'll keep this empty and hide it from the CK.}

You will want to fill both of these arrays with at least 3 items in the Creation Kit for demonstration. Our goal is to build a two item array at sArray, where the index 0 is wArray's values and index 1 is abArray's values.

Building the nested array

We now want to ensure sArray is large enough to accept our items. Note: You can define array size dynamically if required with the create functions on the SKSE Utility script.

Event OnInit()             ;This will fire as soon as this script is loaded by the engine. Not recommended for regular use.
  sArray = new String[2]   ;makes a new array at sArray which is two entries long.

Now we have the array ready, we can set up the function to build the nested arrays. You will need a separator for each form, most common use is a comma. It is worth noting that we will also be storing the items as their decimal FormIDs, as a String can be converted to an Int but not directly back to a Form.

Function BuildNested()
  int iIndex = wArray.Length  ;obtain the maximum value count
  String sTempString          ;set a variable we can use to store the string
  while iIndex
    iIndex -= 1
    Form fTempForm = wArray[iIndex]                ;extract the item from the array
    if sTempString = ""                            ;is this the first loop?
      sTempString = fTempForm.GetFormID()
      sTempString = sTempString + "," + fTempForm.GetFormID() ;prepend the following IDs with a comma

  sArray[0] = sTempString ; write the string into our array

  […Do the same for abArray…]

If you want to check the output of the array to see what it looks like, add this to the end of the "BuildNested()" function.

Debug.Trace(Self + "sArray 0 -" + sArray[0] + " and sArray 1 -" + sArray[1])

So now these values are stored. The above function should print something like this to your trace log "[ArrayProofofConcept <SomeQuest (0000000)>]sArray 0 - 134385746,134385749,134385755 and sArray 1 - 134385746,134385749,134385755"

Retrieving values from the nested array

Now we have our values nested, we need a way to get the back into a usable state. In this case, we'll need to decide which one of the IDs we want to recall.

Form Function RetrieveNestForm(String sSearchString, int iIDtoFind = 0) ;iIDtoFind will be the stored form we want to retrieve, as it was in the original array.
  int iStartIndex ;where to start to pull the Form out
  int iEndIndex ;where to end to pull for Form out
  int iLength ;how long is our Decimal FORMID?

  if iIDtoFind ;If this isn't 0
    while iIDtoFind
      iStartIndex = StringUtil.Find(sSearchString, ",", iStartIndex) + 1 ;looking for the commas, move on 1 space for loop search

  iEndIndex = StringUtil.Find(sSearchString, ",", iStartIndex) ;find the following comma

  if iEndIndex == -1
    iEndIndex = StringUtil.GetLength(sSearchString) ;if no secondary comma is found, it must be the end of the string

  iLength = iEndIndex - iStartIndex

 int iDecimalForm = StringUtil.SubString(sSearchString, iStartIndex, iLength) as Int

 Return Game.GetFormID(iDecimalForm) as Form ;return our value again

When calling this on our current data it would look like:

Form fRecovered = RetrieveNestForm(sArray[0]) ; for the first entry
Form fRecovered2 = RetrieveNestForm(sArray[0], 1) ; for the second entry
Debug.Trace(Self + "found the following IDs:" + fRecovered + fRecovered2)

Alternative Implementation

There is an alternate way to handle nested arrays on the Creation Kit wiki