Jump to content

[LE] Papyrus custom objects and arrays


FlyBy263

Recommended Posts

I'm somewhat new to papyrus scripting but have managed to write fairly large script mod so far. My coding experience is mostly in java so i know object based code.

 

My question is if papyrus supports custom object types and if you can make an array out of them.

 

For instant make a script named TagObject that acts as an object

scriptname TagObject

String Property name = "" Auto
Float Property base = 0.0 Auto
Float Property gen = 0.0 Auto
Float Property walk = 0.0 Auto
Float Property run = 0.0 Auto

So i can have many instances of TagObject all with different values

Then make an array to hold the object such as below

TagObject tag = new TagObject[10]
Link to comment
Share on other sites

No, arrays in papryus don't support that. Arrays can accept all the types in papyrus(ObjectReference, Form, Int, Float, String, Bool, MiscObject, Message, etc). No custom types. You can't mix and match types in an array either, the types must match. If you want a Float array you must create the property in array format, like so:

Float[] property myArray auto
 
; or even a variable and fill it locally
 
Float[] myArray

As a property it will have a special UI in the property manager on the script in the CK for you to add elements. However once it's set, you can't change the elements at run time that way. So it's best to leave it empty and fill it at run time.

 

It may sound restricting, but arrays are fast an efficient, and you can just all kinds of crazy s*** with arrays. However the best practices you may have learned about them in Java, still applies. Infinite loops and all that can still happen.

Edited by Lisselli
Link to comment
Share on other sites

It looks like Form could be universal, but I'm not entirely sure. Casting might be needed. Lots of events have form parameters but accept certain types passed into them. GetItemCount() being an example, or even PlaceAtMe()

 

Forms are the objects defined in the Creation Kit. Otherwise known as the base object. An ObjectReference is a Form that has been(and if it can be) placed in the world. Type casting is something I still don't have a firm grasp of yet, so I don't want to steer you in the wrong direction.

 

Some examples of casting I know for sure:

Integers can be cast to a float and vice versa.

ObjectReference can be cast to an actor if the ObjectReference is an actor.

 

I'm not sure about casting to or from Form.

 

Script casting works in a different way. You can declare an object's script as a property and access its script properties and function in another script. I'll use a quest as an example, since I've worked with these:

Quest A has a scriptname ScriptAQuestScript extends Quest
and I want to call it/s properties and functions in Quest B..
 
; Quest B
Quest property QuestA auto
ScriptAQuestScript property ScriptA auto
 
Event SomeEvent()
   ; let/s say you just want to directly use Quest A/s script without casting.
   ScriptA.ScriptAproperty
   ; but what you don/t? You just need to know Quest A/s script name instead and do this:
   (QuestA as ScriptAQuestScript).ScriptAProperty
EndEvent
Edited by Lisselli
Link to comment
Share on other sites

Papyrus uses a hierarchial system for script object casting. The default + SKSE tree is documented here: https://www.creationkit.com/index.php?title=Category:Script_Objects (It's also a very handy page to refer to when you want to dig up what events or functions a particular script object has available)

 

Anything can be successfully cast to any of it's parent script objects. So, for example, an Actor script object has access to all the functions and events of an ObjectReference or Form, and can be cast to either, but an ObjectReference doesn't have access to Actor scripting and cannot be cast to it, though it can still be cast to a form. Casting from a parent to a child script object can only be done if the thing in question is actually that child object. This is also the only thing that needs to be explicitly cast; Papyrus will automatically cast going up the tree.

 

When you declare a script as extending some other script, you're positioning it in that hierarchy. So if you say your script extends Actor, you'll have access to all the Actor functions, etc. However, obvously, any default functions in Skyrim will only ever return Actor. If they're actually operating on your new script object, you could then cast the Actor to whatever your script is named, and proceed to access any new properties, functions, or events you've added. If you attempt to do so on something that doesn't have your new script on it, the cast will fail and a none value will be returned. This is often used to test for object types in various scripts (e.g. making sure something is a weapon -- you try casting the base form to a weapon, and if you get a none back, it's something else).

 

 

You can create arrays of anything except other arrays, including custom script objects you have created.

Link to comment
Share on other sites

No, arrays in papryus don't support that.

Papyrus doesn't support it "directly", no. But it can be done.

 

Example Quest script (compiled and working):

Scriptname RenTestTagQuestScript extends Quest  

Activator Property RenTagAct Auto

RenTestTagScript[] Property RenTags Auto

int itr = 0

Event OnInit()
	itr = 0
	
	RenTags = new RenTestTagScript[100]
	
	while (itr < RenTags.length)
		RenTags[itr] = Game.GetPlayer().PlaceAtMe(RenTagAct, 1) As RenTestTagScript	
		itr += 1
	endWhile
	
	itr = 0
	
	while (itr < RenTags.length)
		debug.messagebox("RenTags[" + itr + "].TestFloat = " + RenTags[itr].TestFloat)
		itr += 1
	endWhile
endEvent
Example "Tag" script:

Scriptname RenTestTagScript extends ObjectReference  

Float Property TestFloat Auto
String Property TestString Auto

Event OnInit()
	TestFloat = Utility.RandomFloat(0, 500)
endEvent
Link to comment
Share on other sites

In general you shouldn't be thinking in terms of arbitrary data like that.

 

Papyrus is an event-driven, object-oriented language but scripts don't have an existence apart from being connected to game objects (except in the case of global scripts which can't have properties). As Reneer's script shows you can cheat by using some form (like an Activator) as a script holder but script instances are always linked to instances of objects (derived from Form) in the game world or things like quests which have a single global instance. (And for this purpose I'm ignoring the TIF scripts which have an extremely brief lifetime.)

 

If you're coming from a traditional, general-purpose language like Java here is how you translate your concepts into Papyrus.

 

The script file itself is the equivalent of a class definition.

 

Instance methods are the Papyrus functions and events. They are always public.

 

Instance variables are the Papyrus properties and variables. Variables are private instance variables and only provide storage. Properties appear to be public instance variables but despite appearances are really a matched pair of set and get functions that manipulate a hidden variable. The properties are modeled on Visual Basic practice.

 

There are no class variables. The concept simply doesn't exist. If you think you need one you can use one of the singleton objects like quests to fake it though.

 

You can make a class method by tagging a Papyrus function with the Global flag. The Math, Game, and Debug scripts consist entirely of global functions and are really just used to create a namespace for those global functions.

 

All class instances are linked to an instance of some object in the game. For most scripts derived from Form which represent actual things the scripts will be attached to some ObjectReference. Other scripts are attached to special objects like Quests, Aliases, Perks, Scenes, etc. that have a single instance in the game with a global scope and lifetime. Other scripts like TIF (topic info fragments) and those attached to active magic effects tend to have very brief lifetimes and aren't really accessible from the outside in any useful way.

Link to comment
Share on other sites

I've done some shenanigans with active magic effects -- particularly long-duration ones, such as enchantments -- via registering them with an external script, such a quest, that can be stuffed into properties and used to communicate data back and forth. The magic effects, on start, would call a registration function that would add them to an array and then return that array back to the calling magic effect. Because arrays are passed as pointers any other changes made to that array by other magic effects would be immediately visible to all of them, without need to explicitly pass the data back. On finish, same thing in reverse.

 

Using arrays to pass data like that is really fast and I use similar solutions pretty liberally when I need to do performance sensitive stuff.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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