Jump to content
Mattiewagg

[Student Thread] Treeaza - 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).

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

I also highly recommend SSE (Skyrim Script Editor). It's newer than the others, but it's got way more features and is easier to set up. It's got all the same features as ST except plugins, and many more:

Note it's still being worked on and sometimes there will be bugs, like an autocomplete not fully autocompleting, that will cause your script not to compile properly. Thus I will recommend ST for now.

- Edit existing script

- Create new script from within the editor

- Quick compile with Ctrl+S

- Compilation error/success window

- Filter for opening script with options of "Starts with" and "Contains"

- Auto complete (SKSE support as well)

- Syntax highlighting

- Customization of highlighting colors from within the editor

- Clean, professional look

- Extreme customization of all features

- Easy to use

- Resizable/minimizable code and compilation result windows

- Highlighting of words that are the same as selected

- FileDiff tool to show differences between two scripts

- Bookmarking line feature

- Mark line as green/yellow/red

- Multilanguage support (currently English/French)

- Comment out current line feature

- Line cloning

- Column selecting

- Custom autocompletes

- "Draft" save (save without compiling)

- Code folding

- Macro recording

- Line numbers (toggleable)

- Export to "rtf" or "html"

- Various hotkeys for functions, like Go To Line, Replace, and many more

- Auto update feature or update from within editor (Help>About)

- Online help and support

- Tab functionality, including tab all selected lines

- Copy and paste functionality

- Spellcheck on commented out areas and strings in progress

- Etc.

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

I finished reading through those tutorials.  I already had Notepad++ set up, so I will stick with it for now to edit scripts.  I was confused by the word "auto" always being placed in property declarations.  Also I saw the word "Hidden" once.  Are they required for no reason, or do they cause something about that property to change?

As for what I would like to try to do, I would like to try making a spell that pulls the enemy or object that you cast it on towards you, so you can hit them with melee weapons.  I don't know if this is easy enough to start with, but it would be cool.

Share this post


Link to post
Share on other sites

I'll be explaining the auto thing quite soon, as well as hidden. Auto is basically a "default property" - one that can be read and written (got and set). There's a way to make it different than that, or do different things when written or read, and I'll be explaining that in one of the much later lessons, as it's rarely necessary. Hidden means that you can't set the value of the property externally (in the CK) like most properties, only within the script. You can't see it. It has its uses but is also uncommon and will be explained in detail later on.

I'm not very good at spells, since there's other set up required (other than just the script) - but I should be able to help you out on this one.

I'll write a tutorial for the script when I get a chance. Will probably be in a day-two days. The first tutorial of it will just be showing you how to do it and explaining the script. Then, I'll start teaching you the different parts of scripting and you will begin to write your own.

Edited by Matthiaswagg

Share this post


Link to post
Share on other sites

"Pull to me" spell script tutorial

Okay. We'll begin this by creating the effect for your spell (MGEF/Magic Effect), and the spell itself. Every spell is a combination of one or more effects, and it applies the effects appropriately when the spell is cast. I'm not going to give you a full tutorial on how spells work, because I'm not an expert on spells in the slightest. I've dabbled with them some - they're really not my thing. I do scripts and quests.

That said, let's begin. Load up the Creation Kit, and immediately save to make your own plugin. I assume you know your way around the CK a little - so you should be saving a lot. Go to Magic>Magic Effect. Create a new entry, and style it like so (with your own ID for the MGEF):

http://i.imgur.com/DLCJKlr.png

I'm not sure whether you'd rather have a projectile for the effect, and only make it work if the projectile hits, or just apply the effect to the actor being targeted. Choose whichever you'd prefer for Delivery, and if you choose Target Actor don't pay attention to the projectile part.
Aimed: Effect is attached to a Projectile which is then fired at the crosshairs. If it makes contact with a valid target, the effect is applied.
Target Actor: Effect is immediately applied to an Actor in the crosshairs. No projectile is fired. 

Check out Darkfox127's tutorial on Magic Effects for more info.

From there, you want to close the MGEF and then reopen it with OK. This will allow that Script area to be editable, so you can add your script. To make a new script, click the Add button in the scripts area, and choose [New Script], rather than adding a script from the game. Give your script a name - I gave mine myMGEFEditorIDMGEFScript as the name, which is decent naming conventions but you should figure out what is best for you. In BS provinces, we always use a prefix before any names (CYR:Cyrodiil, ELS:Elswyr I think, HF:Hammerfell, MOR:Morrowind, etc. You'll be informed of the one for the province you join). 

Then right click your script, and select Open in External Editor. This should open your script in NP++ with the appropriate syntax highlighting and such. If not, go to Skyrim/Data/Scripts/Source and be sure you associate NP++ with .psc files.

Paste this into your script, excluding the Scriptname part - that part should have the same scriptname as your script. Scripts must have the same Scriptname as the file name of their script, or it won't compile right. Always remember this.

Scriptname PullSpellEffectMGEFScript extends activemagiceffect  

Event OnEffectStart(Actor akTarget, Actor akCaster); names of parameters can be as you see fit, as long as you use the name you declared
	Float casterX = akCaster.GetPositionX() + 10.0; so they don't come TOO close to the PC, you may have to adjust the 10 part so they come at your preferred distance
	Float casterY = akCaster.GetPositionY() + 10.0; don't actually need to put these in variables, could be directly in the input parameters for TranslateTo but it's cleaner this way
	Float casterZ = akCaster.GetPositionZ(); don't want them to be up in the air
	akTarget.TranslateTo(casterX, casterY, casterZ, akCaster.GetAngleX(), akCaster.GetAngleY(), akCaster.GetAngleZ(), 100.0, 75.0)
	; From CK Wiki page: 
	; A movement speed parameter (afSpeed) of 1 is extremely slow - nearly imperceptible to the human eye. Starting at a an afSpeed of 100 and working up or down towards the desired speed is recommended.
EndEvent

As you can see, we have Events (effectively something sent when that thing happens in game, which the script can then "catch" and use) and Functions inside that event - things like GetPositionY(), which do something. We'll cover Events and Functions in the very first lesson. We see that we have two types of variables here - things like casterX, which are normal variables and can only hold a value of that type (Float is decimal, there's also Bool, Int, ObjectReference, etc.). Then we have parameters, which is data sent with the event - and inside those parentheses of the event. Regardless of whether or not you use the parameters, you MUST declare them. Parameters can only be used within that event. If a variable is declared within an event or function, it can only be used within that event or function.

So we're setting our variables to the position of the caster (person casting the spell) + 10, so the target NPC isn't moved literally to the position of the caster (inside them). You may need to adjust this offset until it looks alright. We then move the target to the caster's position that we just found, as well as their angle, at the inputted speed. Those, inside the parentheses of TranslateTo, are also parameters. You must input them in order for the function to take that information and use it correctly. The . in between akTarget and TranslateTo shows that TranslateTo is being called ON akTarget.

As I mentioned, I took some of the information from the TranslateTo page on the CK Wiki, which is an incredibly valuable source of information. You can find the page here: http://www.creationkit.com/TranslateTo_-_ObjectReference.  There is a page like that for almost every function and event, and a bunch of other information.

And then we end the event, so the script knows anything past that is not to be executed when that event is called.

We don't have any properties here, so I'll cover the Properties and Variables lesson right after Functions and Events, since they're used in most scripts.

So, you compile that script (NP++ should let you do it, not sure what key you set  quick compile to but use that one). Then go back to the CK, and we'll attach this MGEF to a spell. Go to Magic>Spells and make a new spell. Give it an ID, a name, and set the Type/Delivery to the same type as your MGEF. Then add your MGEF in the table of the spell area, by adding a new one there and selecting your MGEF's ID from the drop down.

Save that, and go in game. You'll need to use the console (Help "your spell name") to add the spell to yourself, then doing player.addspell xxxxx, replacing xxxxx with the number/letter combination ID that the help command gave you for your spell. Try it out, and report back if it works. It should, provided it's hitting the appropriate NPC.

(This tutorial for this spell didn't really teach you that much about scripting, unfortunately, so I might have you do another or just go on with the lessons - which would you prefer?)

Share this post


Link to post
Share on other sites

That works fairly well.  The speed was too low, so I cranked it up to 500.0, and then ran around pulling in spiders.  So far most of this is fairly close to other languages that I know, so we can probably continue with the lessons.

Edited by Treeaza
  • reaction_title_1 1

Share this post


Link to post
Share on other sites

Related Resources:

Variables and Properties

Properties and variables are things that are able to point to or “hold” values. Variables are only changeable and accessible within the script that declared them, whereas properties are available from any script ADD HYPERLINK, not just the one that declared them. Variables/properties can contain numbers, or things like Objects or Actors. You’re then able to modify the variable/property, directly modifying that value. You can also access the variable/property and rather than getting a constant value, get the current value of that variable or property.

Declaring Variables

Before you can use a variable or property in the script, you have to declare it. Declarations can go just about anywhere after the scriptname declaration.

Int myInt

That’s a declaration of a variable. As you can see, you have to give it a type and a name. You can also do:

Int myInt = 87

Which gives it a starting value of 87. Otherwise, it would be 0/false/0.0/NONE for other variables.

Declaring Properties

Property declarations are somewhat similar to variable declarations, though you can only declare properties outside of events and functions.

Int Property myIntProp Auto

That’s a declaration of a property. It needs that Property part. The Auto will be what you use ALMOST all the time. You can have different flags at the end, which I go over in Advanced Properties 2 ADD HYPERLINK.

Float Property myFloatProp = 20.0 Auto

That’s a declaration of a property as well, but with a default value. Properties are usually set outside of the script, however, by filling the property. That sets a default value, and is important because it allows you to set other default values – like making an Actor property equivalent to a specific actor, which you can’t do with variables. However, you can also make an Integer property have a default value of 16 just like a variable.

Declaration Location

If you declare your variable outside of any events or functions, it can be used anywhere in the script. If you declare it within a function or event, then it will ONLY be accessible within that function or event.

Setting Variables or Properties

To set a variable or property, you simply use the = sign:

Int thisInt

thisInt = 20

;for properties

Int Property thisInt Auto

thisInt = 20

Getting Variables or Properties

To get the value of a variable or property, you’ll have to use it in conditions or assign another variable to that variable:

thisInt = otherInt

myFloatProperty = myOtherFloatProperty

If x == y; to set, you use =. To compare, you use ==

That concludes this lesson.

Edited by Mattiewagg

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 

You'll be making your own script next.

Share this post


Link to post
Share on other sites

Great.

Hello guys, I need a scipt if it's possible !
 
I need a script for magical effect in ck for a custom spell.
 
Script : 
 
1. When release casted spell, script check if target if human and dead
 
2.1 if 1 is true targeted corpse disepear (or teleport un a custom cell) then add item1 in inventory
2.2 else show a notification in top left corner : 'Target Invalid'
 
Thx for help !

You should know all the events. Use the CK wiki (creationkit.com) to find the other functions. The List of Papyrus Functions should be good for CTRL+Fing through to find a keyword (i.e. searching dead to find the function to check if something is dead). 

Share this post


Link to post
Share on other sites

I can't figure out how to tell if the actor is human.  I've checked the CK wiki, but its information on Races is very limited.  Should I just have a bunch of properties of type Race so that the person using the script can set them themselves or is there a better way to tell what they are?

Share this post


Link to post
Share on other sites

I can't figure out how to tell if the actor is human.  I've checked the CK wiki, but its information on Races is very limited.  Should I just have a bunch of properties of type Race so that the person using the script can set them themselves or is there a better way to tell what they are?

​By human, just go with "is it an actor?". So, you can do this to check that:

If (target as actor)

This is technically casting, which we'll go over next. However, in this case - you could just make the spell target actor, but assuming it's not, you'd need that check. The variable is already technically actor, but it's just a good check in case. So you're basically checking - can this be cast to type actor? If yes, it'll be something. If no, it'll be NONE. If (target as actor) is just checking if it can be cast, not comparing it to something else. Alternatively, you could make Race properties for every human race (if we were going just humans allowed, not mer or beast), then do:

If (target == humanrace1) || (target == humanrace2) || (target == humanrace3);etc
;|| is logical OR, && is logical AND

 

Share this post


Link to post
Share on other sites

Here is my finished script:

 
Scriptname DestroyBodyTakeItemScript extends activemagiceffect

{Races of humans:}
Race Property nordRace Auto;
Race Property imperialRace Auto;
Race Property bretonRace Auto;

MiscObject Property Item1 Auto ;Item to add

Event OnEffectStart(Actor akTarget, Actor akCaster)
	if akTarget.IsDead() && (akTarget as Actor) ;Are they dead yet?
		Race targetRace = akTarget.GetRace();
		if targetRace == nordRace || targetRace == imperialRace || targetRace == bretonRace ;Are they human?
			akTarget.Delete();
			akCaster.AddItem(item1, 1, true)
		else
			Debug.Notification("Target Invalid")
		endIf
	else
		Debug.Notification("Target Invalid")
	endIf
EndEvent

 

 DestroyBodyTakeItemScript.psc

 

I haven't gotten to test it yet, because every time I go into the game and try to give myself the spell that I made with it, it says 'Compiled Script not saved'.  I went into the script in the Creation Kit, compiled it, and re-saved it, and it still won't work.  Any ideas?

Edited by Treeaza

Share this post


Link to post
Share on other sites

Have you attached the script correctly and new game? Also, make sure Scripts.rar in your Data folder is extracted (to your data folder). And ensure all the properties are filled:

http://skyrimmw.weebly.com/skyrim-modding/how-to-fill-properties-skyrim-modding-tutorial

The script is correct. ;) Two notes:

If you're not actually going to use a comment after a script line, you don't need the ;. It's not a full stop or anything in Papyrus - those don't exist. It's just for comments.

Also, you didn't need to store the target's race in a variable to compare it. It's actually better that you did (for threading purposes), since otherwise you'd be calling a function 3 times. Just want to make sure you know something can be compared directly to a returning function w/out being in a var. But if you are calling the function more than once to compare, as you are, storing in a variable is better.

Nice job. Gonna post a quick Script Habits lesson before we move on.

Share this post


Link to post
Share on other sites

I had all that done.  I'll play around with the setup a bit more and see if I can get it to work.  I knew about the semicolons, that's just a habit from Java and C#.  Sometimes I accidentally put them after sentences too;

Edited by Treeaza
  • reaction_title_1 1

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:
  1. WhAtiSthisVarEvENdoiNGTomeEYES - You think that's readable? Think again. Please. It hurts to look at.
  2. ALLTHECAPSEVERALLTHETIMEYEEES - Meh. Maybe you're angry, but there's no need to yell at your script.
  3. 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.
  4. CapsForEachWord - It looks nice, makes sense. Clean and simple.
  5. 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

Share this post


Link to post
Share on other sites

I'll stick with the way I did things in Java for spacing and naming.  That is, using camel case for everything except object types, where I use all caps.  As for spacing, it drives me crazy when things aren't spaced correctly.  For a while I didn't use the Visual Studio auto-format because it didn't indent empty lines.  Then I realized you can change that, so now all my code is uniform.  I'm still working on my commenting habits.  Once I had a little game I was working on in Java with (*opens Eclipse to count*) 1481 lines of code and one comment.

Edited by Treeaza
  • 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.  ;)

Share this post


Link to post
Share on other sites

Here you go:

Scriptname SummonableEnemy extends Actor
Float Property healthWhenSummoned Auto

Function Summon(Float x, Float y, Float z)
	; Summons this enemy
endFunction
	

Scriptname OnHitSummonEnemy extends ObjectReference
SummonableEnemy Property enemyToSummon
	
Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
	enemyToSummon.healthWhenSummoned = 10.0
	enemyToSummon.Summon(self.x + 1.0, self.y + 1.0, self.z)
endEvent
	

 

I forget what my original intention was with these scripts, and they don't function, but one does access the other.

Edited by Treeaza

Share this post


Link to post
Share on other sites

Okay, yep, that would work technically. The scripts themselves don't because:

10 is not a float and HealthWhenSummoned is. 10.0 - it can cast itself but why make it when it doesn't have to, as it's a slight optimization to make it 10.0. self.x +  1 won't necessarily be a float, though likely will, so make that 1.0. And I'm not certain self.x is a function but maybe. However, there are functions for getting x, y, and z, hence my hesitation about whether it IS one.

Also the scripts have no function. :P But if the above were fixed they would compile.

But yes - it would work for communicating between scripts.

Share this post


Link to post
Share on other sites

I've fixed that now.  I completely forgot about the Int and Float type difference.  I would have made a functional script, but I couldn't think of what to do.

​No worries. I have the same problem when trying to make you write a script.

Speaking of which - would you like to write another script or keep going with lessons first? We still have to cover things like Arrays and Advanced Properties II, but you're relatively close to the end.

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. 

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

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

Let's see if I can get this one right on the first go:

Int Function FloatToInt(Float toChange)
    return toChange as Int
endFunction

 

​Goal accomplished. Perfect.

Write a quick script (doesn't have to have a reasonable function, but it should be one that would feasibly compile properly) using that function.

Share this post


Link to post
Share on other sites

I made this script for a spell that tells you how much health you have:

 

Scriptname HealthDisplaySpellEffect extends activemagiceffect  

Event OnEffectStart(Actor akTarget, Actor akCaster)
	Float health = akCaster.GetActorValue("Health")
	Debug.Notification("You have " + FloatToInt(health) + " health.")
EndEvent

Int Function FloatToInt(Float toChange)
	return toChange as Int
endFunction

 

Edited by Treeaza

Share this post


Link to post
Share on other sites

Fantastic. Couple notes:

Try to avoid use of convenience functions. I haven't told you this so that's my fault. Convenience functions are things like "GetAV" "GetRef" or "GetActorRef". They, stupid as papyrus sometimes is, call the lengthened version of them self, which in turn calls the actual code, rather than calling the code immediately:

GetAV>GetActorValue>Actual stuff happens

GetActorRef>GetActorReference>GetReference as Actor>Actual stuff happens, cast to actor

GetRef>GetReference>Actual stuff happens

Not only is this (almost imperceptibly) slower, and less efficient, it also means more function calls. Papyrus is a threaded language, so extra function calls COULD, feasibly, slow down your script speed more than expected.

you named your var Heath instead of Health, though it's technically just fine.

The way you wrote the notification debug would, for example, actually notify the player as:

"You have2health".

change it to:

"You have " + floatToInt(health) + " health".

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

Here we go:

 

Scriptname KillArrayOfActorsEffect extends activemagiceffect  

Actor[] Property actorsToKill Auto

Event OnEffectStart(Actor akTarget, Actor akCaster)
	Int on = 0
	while on < actorsToKill.Length
		if actorsToKill[on].GetActorValue("Health") < 50 ; We wouldn't want to kill healthy people!
			actorsToKill[on].Kill(akCaster) ; Kill them, with the caster as the guilty murderer.
		EndIf
		
		on += 1
	EndWhile
EndEvent

 

Just one quick question.  Does Papyrus have for loops, or is a while loop on a counter your only option for doing something several times?

Edited by Treeaza
I forgot to use -= instead of --. Old Java/C# habits.

Share this post


Link to post
Share on other sites

Here we go:

 

Scriptname KillArrayOfActorsEffect extends activemagiceffect  

Actor[] Property actorsToKill Auto

Event OnEffectStart(Actor akTarget, Actor akCaster)
	Int on = 0
	while on < actorsToKill.Length
		if actorsToKill[on].GetActorValue("Health") < 50 ; We wouldn't want to kill healthy people!
			actorsToKill[on].Kill(akCaster) ; Kill them, with the caster as the guilty murderer.
		EndIf
		
		on += 1
	EndWhile
EndEvent

 

Just one quick question.  Does Papyrus have for loops, or is a while loop on a counter your only option for doing something several times?

​Only While loops.

That is a perfect functioning script. :) One thing though - in terms of efficiency. You can totally count UP through the array if you like, but you can also count down and remove the necessity of having that Length part every time.

Make on = actorsToKill.Length, just do While on (which will run it While on isn't 0 - it casts the int to a bool there). and then change the on += 1 to on -= 1 and move it to the top, like so:

Event OnEffectStart(Actor akTarget, Actor akCaster)
	Int on = actorsToKill.Length
	while on
		on -= 1
		if actorsToKill[on].GetActorValue("Health") < 50 ; We wouldn't want to kill healthy people!
			actorsToKill[on].Kill(akCaster) ; Kill them, with the caster as the guilty murderer.
		EndIf
	EndWhile
EndEvent

The reason you move the -= part up is because Length is 1 more than the highest element in the array (since 0 is an element), so you want to get to the highest element before starting to avoid a useless loop. You could also make on = actorsToKill.Length - 1 and keep the -= 1 down there if you like.

Share this post


Link to post
Share on other sites

Now, you're going to learn about States. And I'm just going to link a few tutorials for you to read through, because I have never gotten around to making an actual lesson for this one.

http://www.creationkit.com/States_(Papyrus) - Read first

http://www.cipscis.com/skyrim/tutorials/states.aspx 

Tell me when you're done.

Share this post


Link to post
Share on other sites

Information on states has been read.

​Grand. Now - we have script deciphering. It's practice, I give you a script with no names or comments, and you try to figure out what it does (tell me) and then, at the end, tell me what you think a possible use could be.

Hint:

Read up on GetCurrentGameTime, figure out why I'd want to use floor on it. 

It gives you just the day, since the integer part of the global GameDaysPassed is the days, whereas the decimals correspond to hours/minutes/etc.

Scriptname ascript Extends ObjectReference

Spell Property someSpell Auto 
ObjectReference Property marker Auto 
;this IS an enable marker, which is linked to other objects, and when the marker is enabled, so are the other objects, and vice versa
;this is linked to a sound emitter, and a particle effect (on top of a different object, which is not linked)
Actor Property PlayerREF Auto 
{The player, should autofill}
GlobalVariable Property aglobal Auto 

 
Auto State statea
 
 	Event OnBeginState()
 		marker.Enable(true)
 	EndEvent

	Event OnActivate(ObjectReference akActionRef)

		If PlayerREF.HasSpell(someSpell)
			PlayerREF.RemoveSpell(someSpell)
			Utility.Wait(0.1)
		EndIf
		PlayerREF.AddSpell(someSpell, true)
		GoToState("stateb")
	EndEvent
 	
EndState
 
State stateb
 
 	Event OnActivate(ObjectReference akActionRef)
 	
 	EndEvent

	Event OnBeginState()
		marker.Disable()
		RegisterForSingleUpdateGameTimeAt(0.0)
	EndEvent
 
	Event OnUpdateGameTime()
		GoToState("statea")
	EndEvent
 
EndState

Function iAmAFunction(float c)
 
	float foo = otherFunction()
	If (c < foo)
		c += 24
	EndIf
 
	RegisterForSingleUpdateGameTime(c - foo)
 
EndFunction

float Function OtherFunction() global
 
	float z = Utility.GetCurrentGameTime()
	z -= Math.Floor(z) 
	z *= 24 
	Return z
 
EndFunction

So yeah. Tell me, part by part (state by state, function by function/event if you wish), what it does, and then at the end, tell me what you think it is intended to do as a greater whole/

Share this post


Link to post
Share on other sites

Okay, let's see.

 

The script extends ObjectReference.  We have a Spell property, an ObjectReference enable property, the player, and some global variable.

Auto State statea
 
 	Event OnBeginState()
 		marker.Enable(true)
 	EndEvent

	Event OnActivate(ObjectReference akActionRef)

		If PlayerREF.HasSpell(someSpell)
			PlayerREF.RemoveSpell(someSpell)
			Utility.Wait(0.1)
		EndIf
		PlayerREF.AddSpell(someSpell, true)
		GoToState("stateb")
	EndEvent
 	
EndState

Now, we have a state, statea, that we are in by default.  In the OnBeginState() event, we enable the ObjectReference property from before.  When the OnActivate() event is called in this state, we check if the player has the spell stored in our spell property, and if so remove the spell and wait 0.1 seconds, presumably for some effect or something else.  Then we add the spell and go to stateb.

 


 

State stateb
 
 	Event OnActivate(ObjectReference akActionRef)
 	
 	EndEvent

	Event OnBeginState()
		marker.Disable()
		RegisterForSingleUpdateGameTimeAt(0.0)
	EndEvent
 
	Event OnUpdateGameTime()
		GoToState("statea")
	EndEvent
 
EndState

stateb's OnActivate() is empty, possibly to disable it.  In OnBeginState(), we disable the ObjectReference, and  register for an immediate game update.  Then we have the OnUpdateGameTime() event, which will be called shortly after because we registered for the update, which brings us back to statea.


Function iAmAFunction(float c)
 
	float foo = otherFunction()
	If (c < foo)
		c += 24
	EndIf
 
	RegisterForSingleUpdateGameTime(c - foo)
 
EndFunction

float Function OtherFunction() global
 
	float z = Utility.GetCurrentGameTime()
	z -= Math.Floor(z) 
	z *= 24 
	Return z and fall in a big hole!
 
EndFunction

 

In iAmAFunction(float) we first get another float from OtherFunction(), foo,  check if the float passed to the function is less than that, if it is, add 24 to the float passed in, then register for an update in that float less foo.

 

Finally OtherFunction().  It gets the game time, then runs floor on it.  This gives it the number of days passed as a whole number.  By multiplying it by 24 we get hours, then this number is returned.

 

Now, what do I think this does.  I think this is for some sort of device to give players spells.  When it is activated, it clears the spell if they have it, then gives it to them, waits a moment, then goes to a new state which deactivates the sound and particle emitters the objectreference is linked to, then goes back and reactivates them , preparing the device for use again.  OtherFunction at the end returns the number of days that have fully passed as hours.  The purpose of iAmAFunction seems to be registering for an update when the value of c hours have passed since the start of the game, but I can't really tell.

 

Well, that;s my analysis.  We will see how correct it is.

 

PS.  Sorry this took me a while to write.  I had exams, and then the Steam Summer Sale started.

  • reaction_title_1 1

Share this post


Link to post
Share on other sites

Steam Summer Sale ends all.

You're pretty on point here. A few things:

  • Whenever an event is defined an a state, and is EMPTY (or JUST has a comment in it), it is most definitely disabling that event. That way, when you're in stateb, it'll call an OnActivate with nothing in it. If you just didn't have it defined there, as you learned in the states link, then it would default to the one in stateA, which would do something. So yeah - disabling activation doing anything during this time, as you thought.
  • I messed up one thing. The function RegisterForSingleUpdateGameTimeAt was supposed to be renamed iAmAFunction, and that probably would have cleared up a decent amount for you. You can read about OtherFunction and iAmAFunction (GetCurrentHourOfDay and RegisterForSingleUpdateGameTimeAt respectivelyl) here: http://www.creationkit.com/Light_Switch#Script_Explanation They're two incredibly useful functions, though in order to use RegisterForSingleUpdateTimeAt you must have GetCurrentHourOfDay defined in the same script. They're custom functions. That said, we do have a function I put in CYRMasterScript (not sure if you'll be working in CYR) called HoursUntilTimeOnDOW, which will return a float value that is the hours until a certain time on a certain day of the week. Then, you can RegisterForSingleUpdateGameTime, say, the next monday at 9 AM. Quite useful. You can find the function written out here, for reference: http://pastebin.com/78wc3vQw.
  • I'm not sure how "Return z and fall in a big hole" got in there, but that obviously wouldn't compile, just Return Z.

Now, you were pretty close with the guesstimate of what it was. You can only get so specific with a vague, weirdly named script like this. It's the script I wrote for the Ayleid Wells in Cyrodiil, and it has been tested so it does work. It starts in a state where the particle effect and sound marker for the Ayleid well effect is on, when activated, it turns it off (OnBeginState in StateB). If you already have the Ayleid well effect on you, it removes it. It will then add it - restarting the timer on the Ayleid well effect (which lasts 60 secs or something, I didn't make the spell). It will then go to the next state, turning off the marker and the linked sound marker and particle, as I mentioned. At midnight, it will turn back on and be on again.

Incidentally, this was my first script for Beyond Skyrim. I intended to just be freelancing, working on my own project, and doing the occasional scripts for them. Look where I am now! :o 

Share this post


Link to post
Share on other sites

Write a script that removes all items from a container when it is activated by someone in the player's follower faction (called CurrentFollowerFaction), OR the player themselves, placing it in a different container, and then spawns an item near the person who activated the chest (item of your choosing, could be a book, sword, NPC, whatever).

If you need help, feel free to ask.

Share this post


Link to post
Share on other sites
Scriptname RemoveItemScript extends ObjectReference

ObjectReference Property transferTo auto ; Where to store the items after transfer.
Faction Property currentFollowerFaction auto ; Player's followers.
MiscObject Property objectToPlace auto ; What we should spawn.

Actor Property PlayerREF auto ; The player.

Event OnActivate(ObjectReference akActionRef)
	if akActionRef as Actor ; Make sure they are an actor, so we don't have problems later.
		Actor akActorRef = akActionRef as Actor
		if akActorRef == PlayerREF || akActorRef.IsInFaction(currentFollowerFaction)
			RemoveAllItems(transferTo, true, true) ; Move the items.
			PlayerRef.PlaceAtMe(objectToPlace) ; Spawn the object.
		EndIf
	EndIf
EndEvent

 

 

I'm not sure if this is all correct; My compiler stopped working, and I haven't fixed it yet.  Also, I will be away for ~2 weeks on a camping trip, so I may not respond to posts in that time.

  • reaction_title_1 1

Share this post


Link to post
Share on other sites

I'm going to be away for a week or two as well, so perfect timing.

This is all good, you just done need to cast to actor and make another variable for it. Since only actors can be the player or in a faction, there's no need - ObjectReferences can turn into Actors as Actors EXTEND ObjectRefs. 

Tell me when you return.

Share this post


Link to post
Share on other sites

Done reading now.  Threading Anecdote:  When I had just started programming, I was watching a tutorial series on making a simple little platformer in Java.  Everything was going fine, and I was understanding most of it, so I tried to program the jump function myself.  After I found out that it didn't work, I watched to tutorial on jumping.  At the time, I didn't understand what they were doing, but now I have realized that they were creating a new thread every time the player jumped just to process it.  Logic.

  • reaction_title_1 1

Share this post


Link to post
Share on other sites

Threading is fun stuff! :P Actually, it's possible to make Papyrus use more than one thread at once, but it's quite complicated and not necessary for anything but the most complex functions. See Chesko's Frostfall. But, for example - it is quite code heavy and certain parts of it used to take up to 10 secs. With multithreading, it took 1. You can see the tutorials here, feel free to read them:

http://www.creationkit.com/Creating_Multithreaded_Skyrim_Mods

It's pretty interesting, but not required at all - you'll barely, if ever, use it in the usual scripting endeavours. Depends on what you go on to make, really.

Ill be posting the next lesson soon. Refresh my memory (I seem to be really forgetful the past couple of weeks) - are you interested in quest design too?

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. We're nearing the end of scripting!

Share this post


Link to post
Share on other sites
Scriptname AdvPropScript extends ObjectReference

Int onlySetableVal
Int Property onlySetable
	Function Set(Int newValue)
		onlySetableVal = newValue
	endFunction
endProperty

Int TSPYVal
Int Property theSecondPropertyYay
	Function Set(Int newValue)
		TSPYval = newValue + 3
	endFunction
	
	Int Function Get()
		return -(TSPYval)
	endFunction
endProperty

Float onlyReadableVal
Float Property onlyReadable
	Float Function Get()
		return onlyReadableVal
	endFunction
endProperty

Bool strangeVal
Bool Property strange
	Function Set(Bool newValue)
		strangeVal = newValue
	endFunction
	
	Bool Function Get()
		return !strangeVal
	endFunction
endProperty

 

There's one of the stranger bits of code I've ever written.

  • reaction_title_1 1

Share this post


Link to post
Share on other sites

Apologies for any typos that there may be, wrote on phone just now.

Update Functions and Events

As you have seen before (I think, can provide sample code of in use if not), you can call a function (RegisterForSingleUpdate), filling the parameter with the time in seconds that you want an update to be called. In that # of seconds, an event WILL be called which you can use like any other - OnUpdate.

There are a LOT of updates you can register for.

http://www.creationkit.com/RegisterForSingleUpdate_-_Form With the event called being OnUpdate (no parameters for any of these events). This will call ONE OnUpdate event within the script in the interval (FLOAT) of RL seconds you set as the function parameter.

http://www.creationkit.com/RegisterForSingleUpdateGameTime_-_Form With the event being OnUpdateGameTime. This will call ONE OnUpdateGameTime event within the script in the interval (FLOAT) of in-game hours you set as the function parameter. I.e 24.0 is one ingame day.

http://www.creationkit.com/RegisterForUpdate_-_Form With the event OnUpdate. This will keep calling OnUpdate events every interval (FLOAT) of RL seconds you set as the function parameter, until you http://www.creationkit.com/UnregisterForUpdate_-_FormThis function and the following can cause issues (Esp. This one). Read WHY.

http://www.creationkit.com/RegisterForUpdateGameTime_-_Form With the event OnUpdateGameTime. This will keep calling OnUpdateGameTime every interval (FLOAT) of in-game hours you set as the function parameter, until you http://www.creationkit.com/UnregisterForUpdateGameTime_-_Form.

As you can see, some of these functions share the same event. So if you called a single update twice in a script for different reasons, the same event would be called twice (at the appropriate times). You'd be able to differentiate between what was supposed to be happening with some simple variable use though.

You can also call an update inter-script. In Advanced Properties I, I showed the two methods of accessing another script. I mentioned how you can call a function in another script. This doesn't just include custom functions - though not all functions can be called in a script because they're meant to be called on a specific reference, you can do:

(Self as OtherScriptName).RegisterForSingleUpdate(30.0)
OtherScriptName Property OtherScr Auto
OtherScr.RegisterForUpdateGameTime(21.5)

If you register for an update, other code will run event after it's been registered. It doesn't block other parts of the script at all, unlike latent functions.

Latent Functions

There are several latent functions in the game, and I won't go over all of them.

But a latent function performs an action (like any other function) but then PAUSES the entire script its in until done. This means that until a latent function is finished, NO OTHER CODE in the script will run.

The main latent function is http://www.creationkit.com/Wait_-_Utility

You can find a full list here.

Now, sometimes you'll want to use something like Wait because you want to pause the entire script and do nothing until say, 3 seconds have passed after a certain action. Or, because Wait doesn't count time spent in game-pausing menus, you could wait for a fraction of a second so your code won't run until the PC is out of a menu.

It's okay to use as long as you are aware of what it does, and sure that you want it to be doing that.

Share this post


Link to post
Share on other sites