Jump to content

When are Quest Properties populated in Creation Kit initialized?


louisthird

Recommended Posts

Hello community, checking in and I hope everyone is safe and doing well.

 

I have a Quest that contains an Array of Struct Property:

Struct MyInfo
    String name
EndStruct
MyInfo[] Property myInfo Auto

The elements of the "myInfo" Array Property are populated in the Creation Kit (CK).

 

If the Array is populated with 10 elements in CK and an update of the Mod/Quest/Property changes the Array to be populated with 20 elements in CK, then I would like that to be refreshed. For example, I would like Papyrus script code after update to see 20 elements when calling the "Length" method on the Array.

 

I wondered if someone knew off the top of their head when such properties are initialized? Is it when creating a New Game? Is it when the Quest starts?

 

Thanks and be excellent to one another.

Link to comment
Share on other sites

Firstly you can not call an array by the same name as a struct, they need to be different. Unlike C variables are stored case insensative:

 

Struct MyStruct

MyStruct[] Property MyStructArray Auto

 

If you have a savegame with the 10 element static property quest script saved, the ONLY SAFE way(s) to update that is in script to add new MyStructArray elements. An example I have used to do this:

If (pSKK_AOMVersion.GetValue() < 2) && (AmmoData.Length < 21)
	AmmoData.Clear()
	AmmoData    = New AmmoDataStructure[21] ;increase from 20 to 21 in v002
	ScrapData.Clear()
	ScrapData   = New ScrapDataStructure[5]
	GenerateAmmoData()	
EndIf	

Trying to update the script static property with a different array will cause problems as there is no dynamic update handler to resize variables in the save game loader.

Link to comment
Share on other sites

Always good to hear from you SKK and hope you're getting along well.

 

Thanks for catching my syntax error in the example I created, but it provides an opportunity to emphasize the case insensitivity.

 

Too bad about the way to increase it. Unfortunately that restricts the size of a single Array to 128 elements. Of course, the "split big array into multiple smaller arrays" technique can be used if you at least know the upper bound of your Array larger than 128 elements. If only I had classes I could write a "BigArray" class that encapsulated that behavior.

Link to comment
Share on other sites

You have classes, but what you don't have is a simple way to create new instances like MyClass x = new MyClass().

However, you can actually instantiate classes perfectly fine in Papyrus. It's just... different. And there is no such thing as a constructor, apart from OnInit() event which does not take or pass any parameters.

 

1) attach MyClass script to some proxy object which may or may not be invisible. I like to use xmarkers as a base object, but in the end it does not really matter what it is.

2) use PlaceAtMe() or PlaceAtNode() to spawn an instance (ObjectReference) of the proxy object (Form); Possibly do this in a dedicated cell, to avoid cluttering the worldspace.

3) tadaa you have created an instance of MyClass that is attached to the spawned proxy object

4) access the instance like so: (refMyProxyObject as MyClass).Method(parameter)

5) you can repeat this to create more instances

 

The real trouble with all this is: There are no generics. So things get tedious really quick and it's not possible to provide, well, generic solutions.

 

But if you're only going to populate the array in CK, why not make it a const?

 

MyInfo[] Property myInfo Auto Const

 

Const properties are never stored in the save and always loaded from plugin.

This works for me for loading structs from an array, and I can edit them in CK and next time I load the game, the edits are seen by me script.

 

If that's not possible for your use case, an array of arrays is messy. If you don't want to end up with a bunch of sparse arrays but still keep the array-of-arrays directly addressable in a linear fashion, you'll have to compact the array banks after any delete operations. Which is exceptionally slow because you'll have to loop through your arrays multiple times, as you move elements from the higher banks down into freshly opened up spots in the lower banks. So I batch any deletes first, and then compact all the banks in one go. Some sort of index translation mechanism could work if you're ok with having a bunch of sparsely filled arrays, but that's gonna slow down element access, no matter what. Easiest solution is of course not to allow removal of elements. Or when the absolute order of entries in the array-of-arrays doesn't matter.

 

LLFourPlay DLL otoh offers a nice solution to patch maximum array size supported by .Add() and new to whatever you want. It also has functions to directly create some types of arrays with arbitrary size.

Downside is, it requires F4SE.

Link to comment
Share on other sites

niston, thanks for all the great suggestions. They are all extremely helpful. Using Const looks promising but the others would work as well since elements in the array will never be removed.

 

Regarding the Const approach, there is one concern. Does a Const Array allow you to edit the Struct objects in the Array? For example, is only the Array constant or is the Array and the Objects in the Array constant?

 

Code example:

Struct MyStruct
    String MyField
EndStruct

MyStruct[] Property MyStructArray Auto Const

MyStructArray[0].MyField = "hello"

I wonder if the assignment above is allowed? I can go test this myself ... and likely will, but 'tis nice to converse.

Edited by louisthird
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...