Jump to content
Mattiewagg

[Graduate Thread] Trivia - Matthiaswagg

Recommended Posts

Alright. Let's get this started. I believe you have but if you haven't please install the Creation Kit (found under Library>Tools in Steam).
We'll be covering scripting first, as it'll make creation of small and large scriots for use in quests easier, and has a variety of applications not just for quests but also dungeons, NPCs, bosses (unique boss fights), etc.
Now, please read thesetwo tutorials on scripting I linked, if you haven't already. It should help you understand the basics, so what I say should be a little bit comprehensible.  :P
Part 1
Part 2
For scripting, I don't recommend using the built in Creation Kit script editor. It's not all that fun to work with. Especially because if the CK crashes during your writing of a 500 line script, you'll lose all that. If the CK built in editor even lets you write that much. Often it won't. The majority of scripting is done in an exterior program from the CK, except for hooking up your scripts to the game.
Also, syntax highlighting (pretty colors for your code) and auto complete (as you start to write a function or something it'll allow you to autocomplete it, including parameters) and quick compiling will save your life.
So I'm going to highly recommend you install a good text editor for this. Notepad++ and SublimeText 2 are the most well supported editors for Papyrus (Skyrim's scripting language). They're both absolutely free. Sublime Text will ask for you to verify and pay after a free trial, every month or so, but it's not needed to actually pay ever. It's not at all annoying and just a quick Ok every month. I'll briefly outline the pros and cons for you. I'm currently using ST2 though I used NP++ in the past:
Notepad++:
Pros:
- Good support for Skyrim scripting, as well as SKSE functions and SkyUI
- Easy to use
- Nice interface, clean look and good syntax highlighting
- Auto complete is great. Also includes definitions of functions - so it will autocomplete, tell you which mod/script the function is from, what it does (not all definitions are complete but many), and possibly how it's used. This feature is mainly what I miss for NP.
Cons:
- Sort of a pain to set up the quick compiler, which allows you to compile directly from NP++ rather than writing there and then pasting in CK and then compiling there. Note it may just be because I suck really really bad at using the command line/stuff you need to set it up. Some people find it very was, I didnt.
- Can't change color scheme - a few syntax colors to choose from but none radically different
- No extra plugins
Set it up for the CK here: Linky
SublimeText 2:
Pros:
- Super easy to set up
- Support for SKSE, SkyUI, and almost all other SKSE plugins (syntax and autocomplete)
- Changeable color themes
- Autocomplete
- Syntax
- Plugins and more color themes are downloadable with the (free) Package Control. Plugins can be helpful for greater editing of scripts, and color themes are fun to play around with.
Cons:
- Autocomplete doesn't support definitions.
Setup for Sublime Text 2: Linky
Whenever you've got that set up, tell me (along with which text editor you've decided to use). Be sure to associate .psc files, which can be found in Data/Scripts/Source with your text editor of choice and when we do begin be sure to create your scripts and then edit by right clicking and selecting Open in External Editor.
Then tell me anything you were confused about on the tutorials and we'll begin from there. I'll explain what was confusing and we'll begin on a task of sorts - your first script. If you have any preference on what you want the script to do (unrelated to quests for now), please tell me. I'll take it into account as a first or later script to do, depending on complexity. Examples are a series of messageboxes w/ choices, a script on an activator to spawn a creature, a trigger box that when entered will disable a wall (or move the wall if we use a hidden door, but it has to contain the animations for this). There are many more, just tell me what comes to mind.

Edited by Mattiewagg

Share this post


Link to post
Share on other sites

Okay, I think I've got everything set up now. I went with Notepad++ as I already had it installed for editing behavior files.

I think I understood the basic concepts okay overall (though it might take me a bit to get it all sorted out in my head :) ), except for the odd thing here and there.

I'm a little fuzzy on events. I mean I get what an event is and why it's needed, I'm just not quite clear about their syntax/parameters. Like what's the "ak" prefix for? Is it short for "aktor?"  :P

Also is there a particular reason object references should be avoided? Is it an instability/crashing issue? Just inefficient? Or is it just less flexible/easier to break if something gets changed afterward?

And "if" is like a quick "check if these conditions are met" thing but "while" is more "keep doing this thing continuously until the conditions are no longer met?"

As for scripts I'm fine starting with whatever, though I'm sure I'll think of something more specific eventually.

Share this post


Link to post
Share on other sites

Parameters don't really NEED to have that prefix. They can be whatever as long as they're in the proper order - the engine sends the data, you can name it how you like. I'm not really sure why there's ak, but I use it because I like it. :P ObjectReference properties are absolutely fine, but they DO make something persistent. This means it will ALWAYS be loaded, as long as the property is filled. You CAN clear the property, though it's not always prudent/possible to do so. But it's not a huge deal, really - there are hundreds upon hundreds of persistent items in the vanilla game, and its' not much of an issue.

And you're right on While/If.

You might be interested in this thread, though it's very doubtful you'll understand it all at this time:

http://forums.bethsoft.com/topic/1409991-best-practices-papyrus/

Give me a little and I'll figure out a script for you. :D

EDIT: Tomorrow morning. Going to bed now.

Edited by Matthiaswagg

Share this post


Link to post
Share on other sites

Okay, we'll be starting out with essentially the same schedule/lessons as Minerman. We'll have your first script, which I will write for you and explain, as well as show how to hook up and set up. This will show you a working, albeit relatively simple, example of scriots and their application. It should also provide a good overview of structure.

Then, well actually have a few lessons - one-two on functions, events, and accessing properties from other scripts. If there's something else you're interested in we'll also go over that. In between each lesson I'll have you write a bit of code pertaining to the lesson, so I'm sure you understand it. Then I'll have you make your own script from scratch, based off of what you've learned. If you need guidance with that I'll be able to help, but I'll not do it for you.

From there we'll go over more advanced methods, some other important stuff, and have you create a few more scripts along the way to solidify what you've learned and flesh out ideas of what you can do. And of course, I'll answer any questions and explain anything you're confused about along the way. Near the end of this scripting part (though of course you can ask me about it during the quest design part as well, though well not be focused on it), you can ask me about any scripts you want to create and I'll guide you through them. You can practice as long as you like during this time period to become proficient at scripting.

Sound good? I'll think on a script tonight and tomorrow morning.

Share this post


Link to post
Share on other sites

Okay, apologies this took so long. We're going to begin with a simplish script - one that will add an item to the player every time they click an activator, the amount of items being the amount of times the player has clicked the activator.

Will write up tomorrow, most likely. I've been very sick recently and sleeping a lot.

Share this post


Link to post
Share on other sites

Activator script depending on how many times the player has clicked

Step 1: Setting up the activator

First, find an activator you'd like to use. The easiest one for testing, which Beth used for testing purposes as well, is called WETempActivator. You can find it under WorldObjects>Activators and use the Object Window Filter to search for it by name.

Once you've found it, pull it into any cell in the world. It doesn't matter which, and no need to worry about compatibility. Double click the activator, and go to the scripts tab. You'll have to keep clicking the right arrow until you find the scripts tab.

Screenshot_165.png

Step 2: Making the Script

Once you get here, click Add. Wait a little on the CK, and when a window pops up, press [New Script]. Give your script a name, and keep the Extends part the same.  Then press OK. This will automatically create a script (source) file, which is the editable file, under Data/Scripts/Source. It will also create the "header" for your script:

Scriptname MyScriptName Extends WhatItExtends

So once you've created your script, right click the script on your activator, and press Open In External Editor.

Screenshot_166.png

Since you've associated .psc files with your text editor of choice, this should automatically open the script in that text editor.

Paste in this script, and we'll go over it:

Scriptname TriviaExampleScript extends ObjectReference  
ObjectReference Property ItemToGive Auto 
Int count 
Event OnActivate(ObjectReference akActionRef)
	count += 1
	akActionRef.AddItem(ItemToGive, count)
EndEvent

Firstly, do NOT overwrite your scriptname part (automatically made by the creation kit) with MY scriptname. Each scriptname is unique to the script, and MUST match the actual file name, or there will be compilation errors. Thus, unless you named your script TriviaExampleScript, this will not let your script compile if you outright pasted the entire script in. So replace my script name with your script name first.

So, we declare a property first. 

Type Property PropertyName Flags

The property type says what sort of thing/reference this property points to. You have to know what type of thing your reference is, or you won't be able to fill the property with it. It will simply not show up.

This tells the script it is a property and to treat it as such.

This is how the property is referred to in the script. Something short and sweet (and something that you can associate with the thing) is good here. 

You'll generally just want Auto here. This makes it a normal property which has a value that can be used, and set. No need to really worry about this yet. I'll explain it in a separate lesson.

Then we have our variable. We use an integer variable, because we're just counting numbers with "count", thus the Int part. The name of the variable is count. We didn't make a property, because we didn't need one. If you can easily use a variable instead of a property, you always want to. There's nothing bad about properties, but unless you can't get the reference of something in the script with a variable or you need to use the value of the property from a separate script, you'll want to use a variable. Properties take up more space, and in the case of ObjectReferences, can make an object persistent. There are a few exceptions to this rule, but we'll go over optimization and persistence at a later date.

Our Event is onActivate. This event will be run EVERY TIME this particular activator, WETempActivator in whatever cell you chose, is activated. The stuff in parentheses is a parameter. It is a variable, which you can reference in the script. The variable contains the "value" of the actor that clicked on/activated the activator. We could probably assume this is the player, but just to be safe and because there's no reason not to here, we'll just use the variable akActionRef to reference who did the activating.

The name doesn't have to be akActionRef - you can name it whatever you want, as long as it's in the same order and prefixed with ObjectReference (the type of variable).

Within that event "block", is everything between Event and EndEvent. All this code will be executed when this event is "called" (in this case, when our object is activated). So first, we SET the value of count to the current value of count, + 1.

+= 1 is the same as count = count + 1, just short hand. So we're adding 1 to count. You can find info on other operators here.

Then, we call the function AddItem ON akActionRef. AddItem is a function that will add the specified item (in parentheses), and the amount of items is also specified in parentheses. The . after akActionRef says that this function is being called ON the reference that is akActionRef. The function AddItem is being called ON an ObjectReference, and an Actor is an ObjectReference, thus adding the specified object to the Actor/ObjectReference the function is being called on.

In simple terms, the akActionRef. part says that the item is getting added to the actor that is akActionRef. :)

There are multiple parameters for AddItem. They are:

AddItem(Form akItemToAdd, int aiCount = 1, bool abSilent = false)

akItemToAdd is of type Form, meaning it could be almost anything. An ObjectReference, a MiscObject, a Weapon, an Armor. This is what will be added to the ObjectReference the function is called on. aiCount is an integer parameter, and a default parameter. That = says that if you don't specify aiCount, it will be 1. abSilent is a boolean (true/false), and whether or not the function will display the little message "____ has been added to Player" when the item is added. By default, it will NOT be silent, thus displaying the message. abSilent is also a default parameter.

We put in this:

AddItem(ItemToAdd, count)

Since we increase count by 1 every time we activate the object, and declaring an int variable automatically defaults its value to 0 unless you set it otherwise, this means that it will add our object to the player the amount of times they've added the item.

If you want to add gold, change the type of our property to MiscObject. Gold is a MiscObject and will not show as an ObjectReference.

Step 3: Hooking up our script to Skyrim

Now compile your script. If you had no properties, then that would be all you needed to do. Since we DO have a property, there's one small, extra step. Go back to the CK, and right click our script on the activator. Rather than pressing Open in External Editor, press Edit Properties. Then, click on ItemToAdd in the window that pops up, and choose the item you want to add from the dropdown. 

Press OK, and go test out your script! 

Apologies for the length. If you feel you didn't need something so in-depth, tell me, or if you feel you need more details then tell me that as well. We may go more in-depth and make this a more complex script later on.

Tell me when you're done and if you have any questions.

Share this post


Link to post
Share on other sites

Ok, everything went as expected until I went to choose the item. It gave me this:

post-5510-0-75196100-1419629718_thumb.pn

So I tried changing the property type to "Book" and I got this:

post-5510-0-19307800-1419629719_thumb.pn

I tested it in game and it seemed to work as it should.

(I had tried the "Form" type as well, but it only gave me two choices: CommandingActorPersistenceForm and PapyrusPersistenceForm).

The last few days have been a bit brain-frying for me, so it's entirely possible I've missed something obvious.

Share this post


Link to post
Share on other sites

ObjectReferences need to be in game, as they're references. Unfortunately. However, you should be able to choose cell (any) and then choose your object as needed. 

Plus, I was incorrect in that property. It's most likely a different type of property. What are you tyring to add?

MiscObject

Book

Weapon

Armor

Share this post


Link to post
Share on other sites
I just set it to add the firebolt spell tome. Here's how the script ended up:
 
Scriptname TriviaScript01 extends ObjectReference  
 
Book Property ItemToGive Auto 
Int count 
 
Event OnActivate(ObjectReference akActionRef)
 
count += 1
akActionRef.AddItem(ItemToGive, count)
 
EndEvent

Share this post


Link to post
Share on other sites

Lesson on Functions and Events

Resources:

All About Functions (highly recommend you read, as I'm going to have the functions part of this shorter since this is very extensive + examples and written by myself before)

Events Reference

 

Functions

As you probably know by now, functions make up a lot of your scripts. They make stuff happen, for the most part. Moving things, changing things, getting values, etc. Those are ALL functions. 

 

A function consists of 5 parts:

Type Function FunctionName(Parameters)
EndFunction 

The function type is used for functions that return values, not functions that only DO things and don't return values (if you have one that returns values and does stuff, it should have a function type). As you know, Show is a returning function. It returns an integer - the button chosen. Thus, it is an Int function.  If it weren't a native function (to be explained later), then it's function declaration would have looked like this:

Int Function Show(float afArg1 = 0, float afArg2 = 0, float afArg3 = 0, float afArg4 = 0, float afArg5 = 0, float afArg6 = 0, float afArg7 = 0, float afArg8 = 0, float afArg9 = 0)
     ;what the function does and returns
EndFunction 

The function part is just a function declaration, telling the code that THIS is a function. It's just like the "Property" part of property declarations. The FunctionName part is just like the PropertyName. The Parameters are variables that are passed into the function - so you can have an Actor parameter, Int parameter, etc. This parameter can be used within the code of the function, no matter WHERE you call it from, unlike with global functions (discussed later); which can only use the parameters and nothing from within the script they were declared in. So if I have the function HealthierActor, and it compares the healths of two actors, then I'd have to pass in the two actors as parameters:

Function HealthierActor(Actor actorA, Actor actorB); parameters can be named whatever you like
     If actorA.GetActorValue("Health") > actorB.GetActorValue("Health")
         ;whatever code you want here
     ElseIf actorA.GetActorValue("Health") < actorB.GetActorValue("Health")
         ;whatever code you want here
     Else; is equal
         ;whatever code you want here
     EndIf
EndFunction 

 

And then, you must close it off with an EndFunction, just like you would close off an event with EndEvent.

 

Now let's talk about Return values. You can have your function Return a value, which you can then use. You could assign an integer to the return value of a function that returns an int (or float, or bool, for that matter - see Casting though we'll talk more about it later if you want). Examples of Returning functions are functions like Game.GetPlayer(), which returns the Actor of the player so you can easily use the player in a script. You must have a function type if you want your function to return a value. So, while this will NOT work:

Function ThisIsAFunction()
     Return 10 + 10
EndFunction 

But this WILL:

Int Function ThisIsAFunction()
     Return 10 + 10
EndFunction 

You can also return the value of a parameter, global, variable declared within a function, or a property within a script (IF it is not a global function).

 

Read the Resource All About Functions for more examples, and info on Native and Global functions. 

 

Note that if you have a convenience function which is just there so you don't have to retype code a lot, it doesn't mean the code isn't still happening. If you call a function with 30 lines of code in it 100 times in a script, that's still 3000 lines of code being called (not necessarily a bad thing). It's just you typing out only 30ish lines. So it's not more performance friendly, but it's also not less so. It's the same as if you had typed out all the lines, except the file size is smaller and you don't have to type as much.

 

Events

Events are a bit simpler than functions. As I've said before, they're called when an event in the game happens. They often, but not always, include parameters. Parameters are variables sent along with the event, which you can manipulate within that event. (If you want to use a parameter throughout a script and not just in the event, then you can assign the value of one variable to the value of a parameter.) They look like this (events):

Event EventName(Parameters)
EndEvent

There's not a much that you don't already know about events, to be honest. If you have any questions about them, feel free to ask. There are a few exceptions/things to note, however:

 

- Don't pile up too much in an OnInit event. It can overload it. It's a rather fragile event, though I know of no others like it. If there's more than a few function/code being called in there, use a RegisterForSingleUpdate(0.1) and then put all that code in the OnUpdate event.

- You can call an event directly. So, you could just call OnUpdate() like it were a function, rather than RegisterForSingleUpdate, if you wanted. Or, you could call any other event and pass in it's parameters. Etc. I've not experimented with this much but I've heard from others more experienced than I, that it is possible.

 

That's all I can think of for now. If you have any questions whatsoever, want me to clarify on anything, etc. don't hesitate to ask. :D 

 

Also, I'll probably have you be making your own first script next, but if you'd prefer to have a lesson on say, Casting (how a Boolean turns into an Integer, etc.) or on Advanced Properties (accessing properties from other scripts, properties that can be set but not "got" - value of the property gotten) then we could do that instead. Whichever you prefer. We'd end up doing those lessons later on anyway and then making another or improving your first script to use those concepts. After that we'll do Arrays/Formlists and maybe a few other lessons on advanced concepts with you creating scripts scattered in between.

 

EDIT: Don't ask me why I used that color. That yellow is... not easy on the eyes. Sorry.

Share this post


Link to post
Share on other sites

Okay, I've got one.

Try your hand at a script that will be placed on a miscellaneous object (this is created under Items>MiscObject). When the object is equipped, it will create an enemy where the player is (a little offset so it's not uh... INSIDE the player). It will only create this enemy IF a random integer between 0 and 100 is bigger than or equal to 50. Otherwise, it will damage the player's health by 50. You can choose the enemy. Just one enemy, for now.

=========================

If you have any questions, don't hesitate to ask. If you're looking for functions, I recommend having a look at this page on the CK Wiki. I'll give you two hints here:

http://www.creationkit.com/DamageActorValue_-_Actor

http://www.creationkit.com/RandomInt_-_Utility

And when you get a chance, read this:

=========================

Once you're all done, show me the script. I'll point a few things out (if necessary) until it gets to a state where it should work. Then you can attach it and test it out in game. If it doesn't work, I'll help you troubleshoot.

Few common problems/mistakes:

  • Make sure you fill in your properties

Erm. That's about it. ;) Good luck! Take your time and don't stress about it. I recommend laying out what you think you will need based on what I've said first, without worrying about the details. It'll help you write it out in code form.

Share this post


Link to post
Share on other sites
Okay, I think I got it:
 
Scriptname TriviaCursedSkullScript extends ObjectReference  
 
ActorBase Property EnemySummoned Auto
 
Event OnEquipped(Actor akActor)
if Utility.RandomInt() >= 50
akActor.PlaceActorAtMe(EnemySummoned, 4, None)
else
akActor.DamageAV("Health", 50.0)
endif
EndEvent

Share this post


Link to post
Share on other sites
Okay, I think I got it:
 
Scriptname TriviaCursedSkullScript extends ObjectReference  
 
ActorBase Property EnemySummoned Auto
 
Event OnEquipped(Actor akActor)
if Utility.RandomInt() >= 50
akActor.PlaceActorAtMe(EnemySummoned, 4, None)
else
akActor.DamageAV("Health", 50.0)
endif
EndEvent

Nice! Well done. You even found PlaceActorAtMe, which, I admit, I didn't even know was a function. :P So fill the properties, and try it out in-game. Feel free to take a video or just report back.

Perfect.

Share this post


Link to post
Share on other sites

Fantastic. I'll write up the next lesson, Advanced Properties, within a few days. It'll discuss the full capabilities of properties, and it WILL get a little more advanced. I'll try to be as detailed as possible in explanations, however.

And the whole "Can't be equipped" message is, unfortunately, not removable in any sensible way. It might not bother you but in case you were wondering. ;)

  • reaction_title_1 1

Share this post


Link to post
Share on other sites

Advanced Properties pt. 1

This will go over accessing properties from other scripts.

As you already know properties need to be set externally. Or rather, from within the Creation Kit. This means that they're the only way to reference items, objects, or references that are in game. They can also contain values of integers, floats, bools, etc. However, so can variables, and they do not need to be set externally and they take up (slightly) more memory. 

There's a reason for this. Properties can be accessed from other scripts, unlike variables. This way, you can share information between scripts, as well as changing the values of properties from other scripts. You can even set the value of a property from scriptB with a variable or another property from scriptA. We have 2 ways of doing this, though both are not always viable.

Through script property You can create a property for another script, and then reference that property to reference the other script through it. That sounds rather confusing, but you're essentially creating a property that is filled with the value of another script. So, like how you can fill a Weapon property with a Weapon entry, you can create a property that is the TYPE (a type is something like ObjectReference) of your script. Then you fill the property with your script, and it allows you to access any properties and functions in the other script, just like how you can access and set information on an ObjectReference.

Here's an example:

Scriptname scriptA extends ObjectReference; can extend anything, it can be any type of script
Int Property myAccessibleProperty Auto; could be any type of property
Event OnInit()
;codey code
EndEvent
Function SillyFunction()
;silly function stuff
EndFunction
Event OnActivate(ObjectReference akActionRef)
;moar code
EndEvent
Scriptname scriptB extends ActiveMagicEffect; again, can extend anything
scriptA Property anythingHere Auto; the TYPE of property is the exact name of the script, and anythingHere is just like any property name - it can be ANYTHING
Event OnEffectStart(Actor akTarget, Actor akCaster)
anythingHere.myAccessibleProperty = 29; setting the value of myAccessibleProperty in scriptA from scriptB
​     anythingHere.SillyFunction()
EndEvent

Whenever doing this, you have to make sure you use the proper name for the properties in the other script. Sometimes, you will run into compilation errors when referencing another script, but you can't find anything wrong in scriptB (or the equivalent of it). It always pays to check the other script. Every time you compile scriptB, it will check over scriptA, since scriptB is using info from scriptA.

You DO need to fill the property. There will be a few choices in the dropdown, or just one. Each one is for the object the script is on. So if scriptA is on 100 different references in the game - 100 different weapon entries, for example - you'll have 100 choices in the dropdown. The property refers to a SPECIFIC script each time, so you have to choose a specific one. Just like any other property, leaving it unfilled will ensure the property doesn't work.

By getting the reference the script is on Alternatively, you can get the reference your script is on through function or property, and CAST to your script from that object. So, if you have two scripts on myObjectReference, objScriptA and objScriptB, you could reference objScriptA from objScriptB like so:

Scriptname objScriptA Extends ObjectReference
Bool Property LetSetIt Auto
Event OnActivate(ObjectReference akActionRef)
     ;do some stuff
EndEvent
Scriptname objScriptB Extends ObjectReference; script is on the object that objScriptA is on
Event OnInit()
     (Self as objScriptA).LetSetIt = True
EndEvent

Self is not a function, but sort of like a variable. It automatically contains the reference that the script it is called in is on.

So, what you do is you call Self, and then you CAST to the value of your script. It's a little confusing, but simple in practice. You use a function (other examples are GetOwningQuest(), which we'll use for a lot of quests and returns the value of the quest that a dialogue option or alias is contained within) or "special function" like Self to get the value of the object that has the script to reference on it. Then, you cast to the exact name of your script, and call functions from within that script, or set properties, etc.

MAKE SURE YOU USE THOSE PARENTHESES WITH THIS METHOD! 

Self as objScriptA

Will NOT work. 

(Self as objScriptA)

WILL work.

This method is not always possible if you can't use a function to get the reference of the object the script you're accessing is on. However, it stops you from needing to fill properties, which can save you time. It really depends when you should use one or the other, but you can generally figure it out.

Part 2 will be coming soon and going over the other functions of properties. Part 2 is less useful stuff, but definitely important to know about anyway. I really wanted to get this one out because you've been waiting a while. Tell me if you have any questions.  ;)

  • reaction_title_1 1

Share this post


Link to post
Share on other sites

We're going to begin moving onto more advanced things now. First, Importing and Inheritance, which is a key concept for Papyrus. Casting next, which is another very important concept. Then we'll have a lesson on something you'll put into use far more often - Arrays. I'll have you write a short sample function/script to put Arrays into practice, as well as a little bit of Casting, which is relatively simple.

Finally, we'll wrap up with Advanced Properties pt. II because it's pretty boring and will rarely be used - I've never used the method we'll discuss in there once, but it's still good to know as it will allow you to manipulate properties to a much greater extent.

After that, I'll let you loose on writing a few scripts so I can help you figure out things you don't understand and make sure you've got a good grasp on the concepts. I'll have you go through some vanilla scripts and tell me what they mean, and let you ask any questions you may have. Once we're all through with that, we'll move onto quest design.

(This will be a barrage of lessons because I wrote them on two 14 hour plane flights. 28 hours of time dawdling tends to be good motivation. Just not for the Advanced Properties one because I had no internet and couldn't properly remember it, having never used it.)

Importing and Inheritance

Important Links:

INHERITANCE HIERARCHY

Extending Scripts

Inheritance

As you may know, functions are INHERITED. That little part after "extends†in a scriptname says that a script is a descendant of that extended script. There is a script called ObjectReference in the game, and when you extend ObjectReference, you're really saying that yourScript inherits all functions from ObjectReference.

Generally, you're extending a native script. As you learned in the functions lesson, native functions are defined in C++, and just declared in scripts. These scripts are ones like ObjectReference.psc, or Form.psc.

ObjectReference inherits functions from Form, so it has access to ALL Form functions and ALL ObjectReference functions. This means that yourScript inherits all ObjectReference and Form functions, as well as any functions declared within yourScript.

You could then create yourSecondScript which extends yourScript (would look like Scriptname yourSecondScript extends yourScript in the declaration), and has access to all Form, ObjectReference, yourScript, and yourSecondScript functions. You won't need to use this often, but it's good to know. As seen in Advanced Properties pt. I, you can use properties to access functions as well. You shouldn't inherit from a script if it isn't the right "typeâ€. Like, I shouldn't have a script that would normally extend Quest extend yourScript instead, since yourScript extends ObjectReference.

Importing

You can also import from scripts. Importing is a convenience tool used with global functions (learned about in the functions lesson).

Normally, when you call a global function, you call it like so:

functionScript.functionName()
functionScript being the script the global function is contained in, and functionName being the name of the global function. Two common examples of global functions are Wait and GetPlayer. They are usually called like:
Utility.Wait(float afSeconds); replacing the parameter appropriately
Game.GetPlayer(); returning the player actor
Game and Utility are two scripts in the game with functions declared in non-Papyrus code (C++). Wait and GetPlayer are just two of many globalnative functions declared within them. If you're using Game.GetPlayer() a bazillion times in your script (which you shouldn't do for efficiency reasons - will discuss later), it would save you a decent amount of time to forego that "Game.†part, wouldn't it?

So, you could can instead Import a script, like Game or Utility, and then not need that prefix. You import like so:

Import Game;event or function here, all functions must be called inside them of course
GetPlayer()
With Wait:
Import Utility;event or function here, all functions must be called inside of them
Wait(1.0)
There is absolutely no difference in performance for either method. As said, it's entirely a convenience function.

Tell me when you're ready for the next or if you had any questions on this.

Share this post


Link to post
Share on other sites

Okay, so inheritance is sort of like the scripting equivalent of making an .esp dependent on an .esm, right? Except you can only make it dependent on one other script, unless of course that script is extending another one and so on?

And importing just saves some typing if you're calling functions from the same script frequently. (And the imported script does not need to be the same as the parent script, right?)

  • reaction_title_1 1

Share this post


Link to post
Share on other sites

Okay, so inheritance is sort of like the scripting equivalent of making an .esp dependent on an .esm, right? Except you can only make it dependent on one other script, unless of course that script is extending another one and so on?

And importing just saves some typing if you're calling functions from the same script frequently. (And the imported script does not need to be the same as the parent script, right?)

Exactly. I suck at analogies but that's exactly right.

Right on importing. And no, it doesn't. Keep in mind that some functions have the prefix cuz they're global, but if you're inheriting and its not global there's no prefix. Importing is just for the global stuff. Hence why Kill() in Actor script needs no prefix but RandomFloaf() in Utility script does. It's becausw they're not meant to be inherited - they're misc functions for use EVERYWHERE, not just on actors or quests.

Share this post


Link to post
Share on other sites

Script Habits

For those of you getting this little lessony thing later into their students thread, I apologize. I should've written this first so you settled your habits. Hopefully you're not too set in your ways now. ;)

This will be covering good script habits for NAMING, COMMENTING, and SPACING.

Naming

Naming is very important everywhere, not just in scripting. When you make a new record in the CK, when you make a new variable, function, or property - it's important that you have a consistent and clear naming convention. Records in the CK should have prefixes relative to your mod (Beyond Skyrim has one of these - CYR for Cyrodiil is an example, you'll have to check with your provincial lead to find out theirs) so you can find them with quick searches.

For scripts, prefixes are not as important. Generally, unless editing a vanilla script, you should know what mod properties belong to as they're all contained in ONE (or a few) scripts, which you can prefix. You SHOULD indeed prefix these scriptnames, but the functions/properties/variables do not need to have prefixes.

However, they should clearly and concisely describe what they're doing. If I make an integer variable that I intend to use to store the health of the player's current enemy, "duckytoy†isn't a good name for it. Even if you'll provide yourself a laugh when looking back at your scripts, that'll quickly become an annoyance when you want to understand what that variable's purpose was at a glance. And no - you don't always or even usually remember these things. You'll be making a lot of scripts. So, in this case, enemyHealth would be a preferable name, or something along those lines.

I know some people who just name their variables and don't give a rat's ass what they're called - resulting in something like a Bool called Shitgoesboom (Darren, I'm lookin' at you!). You'll regret it. Unless you're insane. And even then, it'll be harder to be insane because you'll have no IDEA what Shitgoesboom even does. Only to find out after hours of sifting through your script and CK that it really was just used to check if the duckytoy was enabled yet.

I made that way longer then it needed to be, apologies. For naming, aside from clear and sensible names, you'll want to decide how they look. There are 5 choices for you:

  • WhAtiSthisVarEvENdoiNGTomeEYES - You think that's readable? Think again. Please. It hurts to look at.
  • ALLTHECAPSEVERALLTHETIMEYEEES - Meh. Maybe you're angry, but there's no need to yell at your script.
  • camelCase - First word uncapitalized, the rest capitalized. A bit strange but also pleasing to the eye and easy to look at. It LOOKS codey too. Makes you feel more sophisticated. That's probably why I use it.
  • CapsForEachWord - It looks nice, makes sense. Clean and simple.
  • allthelowercases - Please, spare me. The human eye can't comprehend when a word begins and another ends without explicitly knowing the word. What if you made up a word, or there was a name? How would you know when the name began and when it ended?!
PLEASE don't do 1, 2, or 5. I will personally kick you out of the program. Not really. But I will pay less attention to your scripts. ;) After that, it's personal preference between 3 and 4. I like 3, though I used to use 4. Whatever you like. The difference is minimal - duckyToy vs DuckyToy; playerHealth vs PlayerHealth. Capitalization has no effect in Papyrus so type away. Prettily.

Commenting

Jesus, how did I not make it to this section faster? Commenting can be done in one of two ways:

{Comment in here}

; comment over here

The first CAN be used anywhere but is special in the sense that, when underneath Properties and your Scriptname declaration, it will show as a tooltip in the CK. So, if I create a comment underneath the declaration of exampleScript:

Scriptname exampleScript extends duckyToy
{It's an example!}
 

Then when I hover over exampleScript in the CK, a tooltip will appear telling me It's an example!. This can be helpful to keep track of things and remember what things are without ever entering the sometimes ugly, sometimes beautiful world that is your script.

The usual comment is just past the semicolon, like so:

Event onActivate(ObjectReference akActivator); well, that's a perfectly respectable event
 

Anything past that semi colon will be ignored by the compiler. It's strictly for the benefit of the reader or writer of the script.

But comments are IMPORTANT. You need to get into a habit of leaving them, ASAP. You should write comments everywhere in your scripts, to say what blocks of code are doing, what a property is supposed to do or what its respective values may mean. Even past a small line of code, it's good to say WHAT that line of code does if it's not extremely obvious (I mean to anyone - not YOU at the time). That way, if you go back and look into your script, you don't need to redo everything in your head because you're thinking "WTF was this doing here?â€. It can keep your script more organized as well.

Spacing

Spacing is also quite important to the readability of your script. By spacing, I mean when to use TABs (usually 4-5 spaces) and line breaks (hitting ENTER). Tell me - how easy is it for you to decipher this script?

scriptname thisScript extends objectReference
int dumbo
bool lock
event onactivate(objectreference akactionref)
if dumbo == 20
if (akactionref as actor); btw, this will evaluate to true if akactionref is an actor. Handy condition check
if !(lock); if lock is not true
akactionref.kill()
else
akactionref.disable()
endif
else
akactionref.enable()
endif
elseif dumbo == 30
if lock
while dumbo < 50
dumbo += 1
endwhile
endif
endif
endevent
 

Because it compiles (I think). It does. All the endifs are there, all the endwhiles. Even the endevent. But… The lack of space (tab) makes it incredibly difficult to read. Here's a comparison example, two fragments:

int smarty
 
Event onActivate(ObjectReference akActionRef)
    If smarty >= 30
         If (akActionRef as Actor)
              While akActionRef == Game.GetPlayer(); if this was the player then this would be an endless loop - don't use this code ever
    Smarty += 40
EndWhile
         EndIf
    EndIf
EndEvent
 

VS

int smarty
Event onActivate(ObjectReference akActionRef)
If smarty >= 30
If (akActionRef as Actor)
While akActionRef == Game.GetPlayer(); if this was the player then this would be an endless loop - don't use this code ever
  Smarty += 40
  EndWhile
            EndIf
EndIf
EndEvent
 

So don't be the second person. Please. Decide how you want your code formatted, and keep it consistent. More line breaks (like one between Ifs, stuff inside If blocks, Whiles, stuff inside Whiles, Events, stuff inside Events, etc.) does mean a larger file size for your script - BARELY. We're talking a couple KB or less for your source file, depending on the number of lines in your script, and the compiled .pex is smaller than the source. But if you freak out about that, just note it and adjust the way you want to write your code.

My advice - never sacrifice the tabs. :D

Edited by Matthiaswagg

Share this post


Link to post
Share on other sites

Casting

Casting is converting one type of property or variable to another. Here are some examples of types:

  1. Int

  2. Bool

  3. String

  4. ObjectReference

  5. Weapon

The list goes on. These are all TYPES and can be used for properties, integers, or as function return types (detailed in the functions lesson).

Some functions require certain type inputs. For example, the command Wait (which is a global native function in Utility, so you'll need to call Utility.Wait or import Utility to use it) requires the float parameter afSeconds to be used:

Utility.Wait(float afSeconds); to be called:
Utility.Wait(1.0); since 1.0 is a float, it is a valid input

However, you could also put in an integer value instead of a float:

Utility.Wait(1); same as 1.0)

This is automatic casting. A float can be automatically cast to an int without any extra input required from you. It IS (incredibly slightly) less efficient, so you should use a float instead of an int if it's asked for, since it's less to process. But, it does work. There are other examples of automatic casting - bool to int and vice versa, stuff like that. However, to cast you generally need to do so "manuallyâ€. I'll use an example - GetValue(), called on a variable/property of type GlobalVariable, returns the float value of  that global. This means that you CANNOT do this:

Int foo = myGlobal.GetValue()

Without compiler errors. You CAN, however, do:

Int foo = myGlobal.GetValue() as int

That as is how you cast. Not everything can be cast - that can generally be found out by using common sense and the compiler (it'll say types aren't compatible if it won't cast). Sometimes, if you try to cast something that isn't "castableâ€, it'll just cast to NONE. So casting a Weapon to a Bool will just make that Weapon variable = NONE, which is as if the property or variable did not have a value, because it won't.

Casting can be a little tricky, but you should be able to get the hang of it. I'd like you to write me a returning function (see the functions lesson if you need reference) which will return the integer version of a float inputted as that function's parameter.

Share this post


Link to post
Share on other sites

Arrays

Arrays are an incredibly useful tool  that can save you time, many lines of code, and allow for more dynamic usage of scripts.

An array is a type of variable or property - not in the way an Int is a type. It's a modifier of them, essentially.

  1. Int Variable > Int Variable Array

  2. Weapon Property > Weapon Property Arrar
  3. Float Property > Float Property Array
  4. Bool Variable > Bool Variable Array

Etc. An array is a collection of properties/variables. There are various functions to cycle through them and do something to each element.

Arrays can be declared two ways. One, they can be declared by creating a property in the Properties window of your script and checking the Array box when doing so. Or, the variable/property can be declared like so:

Bool[] BoolArray
;or
ObjectReference[] Property ObjRefArray Auto

You can change any property or variable into an array by adding those brackets.

You can't do things to an array like you normally would for a property or variable, since they consist of more than one property/variable (again, elements). Each element is assigned an index - a number (integer) - which is used to do things to that element. The first element in an array is 0, and counts up by 1 from there. This means that the highest element will have an index 1 less than the length of the array. You call upon elements like so:

ObjectReference[] Property ObjRefArray Auto
 
ObjRefArray[0].Disable(); disables the first ObjectReference element in the array

You can put any number in there and have it work, provided there is an element at that index (else, nothing happens and Papyrus will be sad and possibly angry, but it won't break). You can also put in a variable - so let's say I have a list of random potions I want to give the player when my script is initialized (doesn't matter what the script is on, so I'll leave the scriptname part out). I fill an array with all the potions, and then:

Potion[] Property PotionsArray Auto
 
Event OnInit()
Int x = Utility.RandomInt(0, 20); don't leave out 0, it's an index in the array. I'll just pretend there's 20 elements
Game.GetPlayer().AddItem(PotionsArray[x], 1)
EndEvent

There is another function that will allow you to manipulate arrays. It's not exactly a function in that there's no parameter and you don't even have to use parentheses. It's sort of like Self. It's called Length, and is used to get the number of elements in an array.

PotionsArray.Length

You leave out the brackets on the array because you're just calling it on the property itself, not an element. Length is a function/special function for the whole array.

One way to use Length would be something to improve that little script with the potion array we wrote earlier. Rather than picking a range ending at 20 for our integer, which has the problem of needing to be changed each time I add or remove from the array and me needing to know there are that many elements in the array, which I might not always, I can just use PotionsArray.Length and use that as the parameter for the end of the range:

Int x = Utility.RandomInt(0, PotionsArray.Length)

You can combine this method with a While statement to do something to every element in an array with only a few lines of code.

Whiles

I'm not sure if I've mentioned Whiles to you before, but they're sort of like Ifs. They have blocks (begun by While and ended by EndWhile, like If/EndIf). There needs to be a condition in the While declaration - While x > 10, for example. Then, all code within that While block will be run until that condition is no longer true. Be sure to avoid Whiles that will never end. These are called infinite loops and are quite likely to crash the game. Check your conditions, basically. Don't do something like "If 1 < 2†or anything that will always evaluate to true and never change.

Back to Arrays

So, we can combine Whiles with Length to do something to EVERY element in an array with minimal effort. Let's say we have 100 guards that we want to enable. It'd be a pain to have 100 lines of code that "Guard1.Enable()†"Guard2.Enable()†"Guard3.Enable()â€. Even if copy pasting, you'd still need to change the name for each and it would still be 100 lines of code that you could have achieved in 5:

Actor[] Property Guards Auto
Event OnInit(); can be any event or function
Int index = Guards.Length; doesn't have to be called index but I prefer that
While index; while index doesn't = 0
Index -= 1; take 1 away from index - remember, the highest element in an array is 1 less than the length, so we have to start with 1 less than the length to begin with the last element
Guards[index].Enable()
EndWhile
EndEvent

As long as the index isn't at 0 (done), the while will keep looping - subtract from index, enable guard at the new index number. Repeat.

Whole lot better than 100 lines of code, isn't it?

Write me a script extending anything you like which will Kill every actor in an array (however long you like, in whatever event) UNLESS that actor has health bigger than or equal to 50. I'll leave it up to you to find the appropriate functions - I recommend the CK Wiki, as always.

Hint: (Read after you submit your script, but don't change it once

 

Health is an actor value. Look for functions that do things to actor values. If you can't find it, search the wiki for List of Papyrus Functions and Ctrl + F it for ActorValue.

Hint2:

 

There should be an If block inside your While, after the index -= part, checking If the actor in the array has a health LESS than 50 and killing him if so. Remember - unnecessary Elses aren't good. Elses should only be used if you have two situations you want. So, you're only doing something if the health is NOT bigger than or equal to 50, so you check If it's lower than that and then kill.

 

 

Edited by Mattiewagg

Share this post


Link to post
Share on other sites
Scriptname TriviaArrayPractice extends ObjectReference
Actor[] Property HitList Auto
Event OnEquip()
	Int Index = HitList.Length
	While Index
		Index -=1
		Float TargetHealth = HitList[Index].GetActorValue("Health")
		If TargetHealth < 50.0
			HitList[Index].Kill()
		EndIf
	EndWhile
EndEvent

Is this right?

Share this post


Link to post
Share on other sites

I've spoken with 1shoedpunk and I'm going to have a Cyrodiil misc quest held for you.

When we start on quest design, which should be quite soon, we'll be building that quest as we go. I've not chosen the quest yet but I will once we reach that point. You won't have a particular time limit as long as it's done before summer.

Quest design concepts will go something like this:

Quest Design Concepts

Preparation

Quest Stages

Quest Aliases

Quest Dialogue

Quest Objectives

Quest Journal Entries (this may be right after stages)

Connecting the Dots

Radiance (we may discuss this earlier depending on your quest)

Some waiting for your quest to be merged so we can move on, we'll talk more about concepts and I'll help you with any questions you have had

Voice Implementation

It'll be more fluid because I'll be teaching as you go.

Edited by Matthiaswagg
  • reaction_title_1 1

Share this post


Link to post
Share on other sites

Okay, your script to decipher:

Scriptname testScript extends ObjectReference
ObjectReference[] Property array Auto
Int I
Event OnInit()
i = array.Length
EndEvent
Event OnActivate(ObjectReference akActionRef)
     If akActionRef == Game.GetPlayer()
          If (akActionRef as Actor).IsSneaking()
               array.Disable()
               If i == (array.Length - 1)
                    i = 0
               Else 	
                     i += 1		
               EndIf			
               array.Enable()		
          EndIf	
     EndIf
EndEvent
Tell me, to the best of your ability, what this script does (line by line). Then tell me how it might be used (to the best of your ability).

It's not a test, and most scripts will have better naming of variables and commenting so you can figure them out better. I'll show you one with good naming conventions and commenting next so you can see the difference. But script deciphering is an important skill, because you'll have to do it a lot for vanilla and mod scripts so you can find out what they do.

Share this post


Link to post
Share on other sites

Pretty sure I messed up somewhere...  :X

Scriptname testScript extends ObjectReference
ObjectReference[] Property array Auto ;declares an array of object references named array[] as a property
Int I ;declares an integer variable named I
Event OnInit()
i = array.Length ;make variable i equal the length of array[]
EndEvent
Event OnActivate(ObjectReference akActionRef) ;when the object is activated
     If akActionRef == Game.GetPlayer() ;and the activator is the player
          If (akActionRef as Actor).IsSneaking() ;and the player is sneaking
               array.Disable() ;disable array[]
               If i == (array.Length - 1) ;check if i is equal to one less than array[]'s length (on the last object in the array)
                    i = 0 ;make i equal to 0 to go back to the first object in array[]
               Else 	
                     i += 1	;add 1 to current value of i which will move it to the next object in array[]
               EndIf			
               array.Enable() ; enable array[]
          EndIf	
     EndIf
EndEvent
;if the player activates the object while sneaking, array[] will move to the next object and, if on the last object, will cycle back to the first one
  • reaction_title_1 1

Share this post


Link to post
Share on other sites

Pretty sure I messed up somewhere...  :X

Scriptname testScript extends ObjectReference
ObjectReference[] Property array Auto ;declares an array of object references named array[] as a property
Int I ;declares an integer variable named I
Event OnInit()
i = array.Length ;make variable i equal the length of array[]
EndEvent
Event OnActivate(ObjectReference akActionRef) ;when the object is activated
     If akActionRef == Game.GetPlayer() ;and the activator is the player
          If (akActionRef as Actor).IsSneaking() ;and the player is sneaking
               array.Disable() ;disable array[]
               If i == (array.Length - 1) ;check if i is equal to one less than array[]'s length (on the last object in the array)
                    i = 0 ;make i equal to 0 to go back to the first object in array[]
               Else 	
                     i += 1	;add 1 to current value of i which will move it to the next object in array[]
               EndIf			
               array.Enable() ; enable array[]
          EndIf	
     EndIf
EndEvent
;if the player activates the object while sneaking, array[] will move to the next object and, if on the last object, will cycle back to the first one

Perfect! You're right.

 

What are some possible uses for a script like this that you can think of?

Share this post


Link to post
Share on other sites

Oh, well nevermind then.  ^_^

I'm not really sure of the uses (I think it's mostly the sneaking part that's throwing me off). Maybe something to do with some kind of trap?  :shrug:

In this case, the sneaking is just a way to ensure the player actually wants to make the change. 

In reality, it is used as an upgrade script. It allows you to switch out an object when activated to any object - a bunch of objects placed on top of each other, and then filled in an array. It's being used in my and Galandil's submission for the house modding contest. ;) It's used for the throne to change it to any type of throne or chair.

Share this post


Link to post
Share on other sites
Advanced Properties pt. 2
The information I have gathered is from the CK Wiki, fellow modders, and other sources. It does WORK but I'm no expert so I apologize if I get anything wrong.
 
IMPORTANT LINKS:
Property and Scriptname Flags - look at full properties
 
We'll be going over the more advanced parts of properties (and a bit about scriptnames).
 
First, we'll be talking about adding more than just "auto†to your scriptnames/properties. There are a few other flags you can have on your properties:
 
Hidden
Conditional
AutoReadOnly
 
These also can be added to your scriptname (after the extends ____ part):
scriptname ____ extends ______ [flag(s)]
For properties, you add flags:
____ Property ____ [flag(s)]
 
Hidden means that the property or script will not be visible within the CK. So, if you make a script called "mahScript†and make it a hidden script (Scriptname mahScript extends ObjectReference Hidden), you can't find that script from the CK. So if I click "Add†to add a new script and search for mahScript, that will not show up. It's hidden from the CK. You can still find the script in the folder relative to the Skyrim directory, and you can still reference hidden properties like you can from Part 1 of Advanced Properties.
 
This is automatically used for something like Papyrus fragments, which is code for dialogue, packages, auto-generated quest scripts, etc. as they all have completely random names and are mostly generated by the CK and not written.
 
Conditional on a script means that properties defined as Conditional within that Conditional script can have their values checked with a condition function (a condition function will be used a lot later - they are not script commands, but how you condition things in the CK like magic effects, spells, dialogue, and more. The particular one for a script is GetVMScriptVariable, but it won't work on ReferenceAlias scripts. For quest scripts, you have to use GetVMQuestVariable). If you define a script as conditional but none of it's properties as conditional, then they won't be able to be checked via those condition functions. If you define a property as conditional but not the script, then none of the conditional properties in the script can be checked. It has to be like so:
Scriptname conditionalScript extends WhateverYouWant Conditional
 
WhateverType Property conditionalProperty Auto Conditional
You can have more than one conditional property in a script, or just one. Only ones with the conditional flag will be checkable. When creating a property with "New Property†in the script properties box, you have checkboxes for properties to become hidden/conditional as well as for the script itself when making the script. You can use those or just tack it on/remove it in the script as you like.
 
Auto read only is much like a normal property (Auto), except it CANNOT be set. The value of an AutoReadOnly property can only be "gotâ€, not "set†or changed in any way shape or form:
Int Property autoReadOnlyProp AutoReadOnly
 
;event
     int x = autoReadOnlyProp; this works, you're "getting†the value and making x equal to it
     if (autoReadOnlyProp == 86); this works, you're checking if the value of the property is 86
          ;code
     endif
     autoReadOnlyProp = 99999; this will NOT work because you're not allowed to set your variable
;endevent
This will rarely be used - I myself have never used this, and it is unlikely you will ever need to in the average or even semi-complex script. However, if you happen upon an instance where you need a "constant†and unchangeable value for a property, this is what you would use. The initial value of the property is allowed. You cannot change it anywhere, however. And you can't change flags on the fly either so your property will always be the same.
 
==========
 
Now, this brings us to something more complex. Conditional and Hidden were both supplementary flags, which can be added to a property (or both added to a property). AutoReadOnly replaces Auto. So what exactly is "Auto†doing?
 
Auto is the simplest way to declare a property. It means that you can get the value of a property, AND set the value of the property. You can do this whenever you want, however you want. It's usually how you'll want a property used.
 
However, you can manually define your property as well. This means that you can completely control what happens when your property is set and got. So, you could say that propertyA CANNOT be set but it CAN be gotten (this is AutoReadOnly so you could just use that flag instead). Or, it could be set but not got. OR, and this is where it gets really powerful, you can completely define what happens when a property is "set†or "gotâ€. What value is actually got? IS a value even sent, depending on a variable? Perhaps setting a property actually sets the value to 2x what you said to set it to.
 
This isn't used incredibly often, and I haven't found just cause to use it yet. However, when you get into more complex scripting environments, it may be of tremendous use to you. It's another way Properties one-up variables - variables can't be manipulated in this fashion, or have flags. They're simpler, which is a downfall and a benefit.
 
Each Property actually has two little functions built into it. When you declare a property as Auto, this is what it's really saying:
int myValue = 0
int Property ValueProperty
  Function Set(int newValue)
      myValue = newValue
  EndFunction
  int Function Get()
    return myValue
  EndFunction
EndProperty
Two things to note: the first is that omitting one function or the other means that it can't be written to (if removing set - can't be set) or read, like AutoReadOnly vars, (if removing get - can't be got). Secondly, removing a Set function means you can't see the property in the editor and thus can't give it an initial value.
 
Something else is that you NEED to declare a variable that is the same type as your property, such as myValue, and reference that in your property. If you don't, there will be a compiler error. This variable should equal the initial value of your property. If you set the initial value in the editor to be different than the initial value of your internal variable, the initial value of your property will be the one set in the editor, UNTIL you Set your property.
 
So, we could make a Property that cannot be set and, when got, actually gives the value of itself + 5, like so:
int testValue = 0
int Property testProperty
  int Function Get()
    return myValue += 5
  EndFunction
EndProperty
 
Any property or local variable declared outside of events or functions can be called within your property functions. Functions can be called within your property functions. Unless you've only got a ReadOnly function (Get only), make sure you've got a variable like testValue/myValue referenced before. The name can be whatever.
 
However, you cannot declare anymore parameters for Get() or Set(). They absolutely MUST look like this in your property:
  Function Set(int newValue)
     ;stuff
  EndFunction
  int Function Get()
    ;stuff
  EndFunction
 
You'll get compiler errors if you do. 
 
These full properties CAN be called from other scripts, just like any other. (See Advanced Properties Part I for reference.) They can be any type of property.
 
I want you to write me one script with 4 different properties. Name them as you like. These should be Full Properties (the ones right after here). The 1st Full Property should be one that CANNOT be read, but only Set. The 2nd should return the negative version of the newValue when read, and when Set should add 3 to the value stated. The 3rd should only be readable, not writable. The 4th should be a Bool, readable and writable, with a normal write but when read it should return the opposite bool of what is currently is.
 
Then, paste the script here and we'll be all done with scripting lessons, with a few more scripts to write and questions to ask. Then quest design!
 
Sorry it took me so long to get this one to ya.

Share this post


Link to post
Share on other sites
Scriptname TriviaProperties extends ObjectReference

Int Value1 = 0
Int Property Property1
	Function Set(int SetValue1)
		Value1 = SetValue1
	EndFunction
EndProperty
Int Value2 = 0
Int Property Property2
	Function Set(int SetValue2)
		Value2 +=3
	EndFunction
	Int Function Get()
		return -Value2
	EndFunction
EndProperty
Int Value3 = 0
Int Property Property3
	Int Function Get()
		return Value3
	EndFunction
EndProperty
Bool Value4 = False
Bool Property Property4
	Function Set(Bool SetValue4)
		Value4 = SetValue4
	EndFunction
	Bool Function Get()
		If Value4 == False
			return True
		Else
			return False
		EndIf
	EndFunction
EndProperty

Okay. I'm a little iffy on that set function in the second property (feels like I should have done something with SetValue2), and I'm not sure if there's a more efficient way to do that fourth one, but at the very least it compiles without issues.  :shrug:

Share this post


Link to post
Share on other sites

For the second, it was adding 3 to the newVal. So it should have been:

Value2 = (SetValue2 + 3)

For 4, the most efficient way for the reading is the following:

Return !(Value2)

! Means NOT, and since a bool can only be true or false, this will return the opposite of Value2. You can use ! quite often:

If myInt != 5; if not 5

Or:

If !(MyInt == 5); if not 5

It's useful.

Share this post


Link to post
Share on other sites

Anything's fine, really. Maybe a spell or a trap or something?

And no, I don't think we covered states.

Alright. Let's do a spell then, one that I've known to be done before. It'll allow me to show you some ways of inputting values for predefined messageboxes too. It'll be a spell that tells the PC the stamina, health, and Magicka of their target, provided that target is an NPC. Before we do the script, you need to set up the MGEF and the spell. Follow DarkFox's tutorial on the subject and when you're done (NOT started on the script yet) post a picture of the MGEF window. We'll be testing the spell just by using the AddSpell console command so all you need to do is make that MGEFS, a spell, and attach the MGEF to the spell.

http://www.darkfox127.co.uk/magic.html - the one that's just talking about MGEFs and spells

DarkFox has a lot of great tutorials which I encourage you to watch when you get a chance. Watching the scripting ones is something I'd like you to do - watch at least 2. Don't watch any quest design ones yet, but all others are fair game.

Take a look at that States link, then. It's not written by me but it should do the trick. Tell me if you're confused by anything.

Share this post


Link to post
Share on other sites

Also, did you read the recommended links on that Advanced Properties II lesson? (For the second, just the part on full properties is necessary.)

Yep, read 'em both. 

Great. Set a hit shader, and write the script.

There are two ways you could write it, but either way I want you to have it so that, when the MGEF is cast, it will notify the player of the target's current health, stamina, and magicka. If it misses the target, or the target isn't an actor (to check that, you can simply do: If (actorPropertyVarOrParameter as Actor)), then it should tell the player "Target not valid".

Show me the script when you're done, then you can test it out.

Share this post


Link to post
Share on other sites

Okay, I think I'm a bit stuck:

Scriptname TRIPracticeSpellScript extends activemagiceffect  
Message Property TargetStats auto
Message Property InvalidTarget auto
Event OnEffectStart(Actor akTarget, Actor akCaster)
	Float TargetHealth = akTarget.GetActorValue("Health")
	Float TargetMagicka = akTarget.GetActorValue("Magicka")
	Float TargetStamina = akTarget.GetActorValue("Stamina")
	If (akTarget as Actor)
		;TargetStats.show()
		debug.trace("Health: <TargetHealth> Magicka: <TargetMagicka> Stamina: <TargetStamina>")
	Else
		;InvalidTarget.show()
		debug.trace("Target not valid.")
	Endif
EndEvent

So I know you're not supposed to use debug messages outside of testing, so I tried using message properties. I'm just not sure about getting the actor values into the message (not even sure I did it right in the debug version).

Share this post


Link to post
Share on other sites

Okay, I think I'm a bit stuck:

Scriptname TRIPracticeSpellScript extends activemagiceffect  
Message Property TargetStats auto
Message Property InvalidTarget auto
Event OnEffectStart(Actor akTarget, Actor akCaster)
	Float TargetHealth = akTarget.GetActorValue("Health")
	Float TargetMagicka = akTarget.GetActorValue("Magicka")
	Float TargetStamina = akTarget.GetActorValue("Stamina")
	If (akTarget as Actor)
		;TargetStats.show()
		debug.trace("Health: <TargetHealth> Magicka: <TargetMagicka> Stamina: <TargetStamina>")
	Else
		;InvalidTarget.show()
		debug.trace("Target not valid.")
	Endif
EndEvent

So I know you're not supposed to use debug messages outside of testing, so I tried using message properties. I'm just not sure about getting the actor values into the message (not even sure I did it right in the debug version).

Good job at least trying with the messages, as that's the best way to do it, though definitely more complex.

Debug.Trace only shows up in the Papyrus log, so you'll want to use Debug.Notification instead. With Notification, you should do:

Debug.Notification("Health: " + TargetHealth + " Magicka: " + TargetMagicka + " Stamina: " + TargetStamina)

The Else was right, you'd just want Debug.Notification, not Trace. You should put EVERYTHING except the Else inside the If, because it's more optimized. That way, it won't perform unneeded functions (Getting the actor values) unless there's reason to.

To do it with messages, you'd use Show, but you'd use the optional parameters for Show (read that whole page), and format your Message accordingly. So your actual message form in the CK would say:

Health: %.0f Magicka: %.0f Stamina: %.0f

And you'd use:

TargetStats.Show(TargetHealth, TargetMagicka, Stamina)

Which is simpler anyway. So try both of them out. :)

Share this post


Link to post
Share on other sites

Ok, it's working fine now, except for the invalid message not showing up.

Scriptname TRIPracticeSpellScript extends activemagiceffect  
Message Property TargetStats auto
Message Property InvalidTarget auto
Event OnEffectStart(Actor akTarget, Actor akCaster)
	If (akTarget as Actor)
		Float TargetHealth = akTarget.GetActorValue("Health")
		Float TargetMagicka = akTarget.GetActorValue("Magicka")
		Float TargetStamina = akTarget.GetActorValue("Stamina")
		TargetStats.show(TargetHealth, TargetMagicka, TargetStamina)
	Else
		InvalidTarget.show()	
	Endif
EndEvent

2015-03-15_00003_zpssxxkf6gw.jpg

Share this post


Link to post
Share on other sites

Ok, it's working fine now, except for the invalid message not showing up.

Scriptname TRIPracticeSpellScript extends activemagiceffect  
Message Property TargetStats auto
Message Property InvalidTarget auto
Event OnEffectStart(Actor akTarget, Actor akCaster)
	If (akTarget as Actor)
		Float TargetHealth = akTarget.GetActorValue("Health")
		Float TargetMagicka = akTarget.GetActorValue("Magicka")
		Float TargetStamina = akTarget.GetActorValue("Stamina")
		TargetStats.show(TargetHealth, TargetMagicka, TargetStamina)
	Else
		InvalidTarget.show()	
	Endif
EndEvent

2015-03-15_00003_zpssxxkf6gw.jpg

Nice! Make sure your property is filled.

Also, for this one, you wouldn't want it to be a messagebox most likely. Make it a notification for both messages by unticking the Messagebox checkbox in the CK form.

Now, do you have any questions at all about scripting or any aspect of it? Anything you want to know? I'm going to have you make a couple more scripts after that, but we're nearing the end (of scripting). :D

Share this post


Link to post
Share on other sites

I feel like I had a question but I forgot what it was.  :X Guess I'll have to think about that...

Also, in a situation where either a bool or an integer could be used, what pros/cons would there be to each? 

There's no pros or cons in terms of efficiency. Bools, you can do the whole !Bool thing. Ints you can't. You have more options with an Int. It really is a matter of preference. I usually don't use Bools, but you can.

Sometimes it may just be more "logical". For example, if you're toggling something in your script, you COULD use an int for 1/0. Or you could use a bool. It's your choice, but a bool would make more sense and be easier to decipher in a script if you hadn't commented your script.

Share this post


Link to post
Share on other sites

So, I went to the Bethesda Softworks Forums (really useful, very experienced modders there) and found another short script for you to write.

The actual thread doesn't matter much - the OP disappeared. But this was his request:

Hi there Mastermodders!

Im at a loss. I've got no experience with coding or scripting whatsoever. But I gave it a try and managed to get a few things working but now when the script will consist and affect more items I'm not so sure anymore.

So I'm simply, in a sad lazy manner, asking someone for help compiling a script doing what I want. Hopoefully I'll be able to work out the logic of scripting by looking at a working one.

So now to the task at hand.

I have a button (Activator01) that when used activates a number of other activators. 2 to be exact (Item01 and Item02). I also want Activator01 to disable another object (Item03).

All this at the same time as playing 3 sounds simultaneously (Sound01, Sound02 and Sound03).

The animation of Item01 and Item02 differ in time so blocking the activation of Activator01 for 5 seconds upon activation would be required.

So far so good.

But this is when it gets out of hand... For me atleast.

I want the script to do exactly this in reverse if activated again. So instead it would reenable Item3 and Activate Item01 and Item02 Again while playing 3 new Sounds (Sound04, Sound05 and Sound06).

If it gets activated again after the reverse it should go back to before.

I know I could've been explaining this all a bit simpler, but now you be able to understand what I'm trying to do!

If anyone could help me work out such a script I would be so happy!

Thanks for your time

Arvtagaren

Take whatever time, how much or how little, you need. The guy actually explained it pretty well for Scripter's to understand.

Be sure to check the CK Wiki to find functions before checking the hints. You can apply at least one advanced concept here.

HINTS

Only if you need them

Use States when activating - going back and forth between Reverse and Normal states

Use http://www.creationkit.com/BlockActivation_-_ObjectReference with Self and properties if necessary

Use http://www.creationkit.com/ObjectReference.Activate_(Papyrus)

Use http://www.creationkit.com/Play_-_Sound

Edited by Matthiaswagg
  • reaction_title_1 1

Share this post


Link to post
Share on other sites

Sorry to intrude, but you may have your script in the wrong place or it doesn't have the correct extension?

Edited by exit

Share this post


Link to post
Share on other sites