Ok, so here is my Scripting 101 primer. This is the kind of thing I had always hoped to find but never had much luck when first learning to code. All tutorials I found out there seemed to be advanced modders who couldn’t put things into simpler terms because they were written by programmers and technical manual authoring is only useful to those who already have a basis of understanding, otherwise it’s really hard to delve in.
See Fig 1.
The top line is basically the title of your script, all scripts start with ScriptName and have your script name (whatever you want it to be) the word extends and another base script. Basically all script functions come from one of several base scripts which come in vanilla skyrim: ObjectReference, Form, Weapon, Actor, ReferenceAlias, Quest, etc are all base scripts which have several functions which affect things in the game when you run them in another script. Since Fig 1 example is ObjectReference, all functions in this script are generally geared towards ObjectReference and use all functions from that script. It can be a little complicated but ultimately it is fairly simple; if you are putting a script on an actor, you use “Extends Actor” or if you are putting a script on an sword you would use Extends ObjectReference if attaching it to the weapon laying in a dungeon, or Extends Weapon if attaching the script to a base weapon FormID.
This is the most complex part that I had a hard time wrapping my head around. This is where you essentially tell Papyrus what the hell everything in your script is.
Rule #1 Papyrus (the Skyrim programing language) has no idea whatsoever what ANYTHING in the game is until you tell it specifically, so everything must be defined as Properties before any kind of script function can work on it.
In Fig 1 you see two quest properties listed and two INT (integer) properties listed. Let’s look at Quest properties first. You see MyQuest and NextQuest right? Well these are just generic proxies for what quest you will be referring to in the script. Since a script can be re-used in multiple places, you define the “quest” with a generic name as show, that way if I use it for “DBM_MuseumQuest1” and then again for “DBM_RelicHunterMission” I simply have to fill in the property
After you write a script and have your properties entered you then actually tell the CK what those properties point to. So after you save and get back to the CK screen with the edited form window open and the script tab with your new script listed, you then hit Properties and define what MyQuest and NextQuest are. If you are only using this script for one purpose, you could instead of MyQuest and NextQuest use the actual quest name and hit Autofill which will automatically assign your quest to that property. What exactly does this all mean? Basically you have told the script that MyQuest is a quest form, and you have told the CK which quest exactly this property points to, so that any time you perform an action involving MyQuest in this script, it automatically knows to refer to your designated quest.
Now looking at INT briefly. Things can get confusing (for me as well) since certain functions work only with INT and some work with FLOAT but both are numbers, so it’s a little confusing, but we won’t go into that. Basically INT are just variables that you put in place of an actual number. Again this is an example of outfitting your script to be multifunctional for use in other places. In the Fig 1 example I use both INT properties as a quest stage, and I have used the INT value as well as simply defining the quest stage.
The first line will set MyQuest to whatever stage you set NewStage to in the properties tab, whereas the second script will merely set NextQuest to stage 10
Hope this hasn’t been terribly confusing so far, just mill over the Fig examples and read over this first section thus far a few times and it should solidify a bit more if it has.
Events are something every script (other than quest scripts) always have (though quest scripts CAN have them, they don’t always). Events are basically telling the script what to watch for in game in order to process the lines of code that follow. In the Fig 1 example the Event is OnTriggerEnter(). This event will run the code that follows any time the trigger box is entered by any actor. The parenthesis text that follows is important to remember. All functions (covered in a moment) and most Events have these which basically are a list of parameters known as Arguments. These are essentially allowing you to define other elements in the course of the script that are affecting or affected by the event or function at hand. In the example akTriggerRef allows you to refer to an objectreference as the instigator of the trigger, specifically in this case to define a condition for WHAT must enter the trigger in order for it to fire. If you look at the line directly after you will see an IF check which is telling the script essentially to abort unless the player is the one who triggered it.
Functions are WHAT actually happens when the event happens. All functions end with (). if they have special argument options they will be entered in between, otherwise they are left empty. If you leave the () off, the script thinks you are talking about a property by mistake.
If you look at the Fig 1 line: If akTriggerRef == Game.GetPlayer() you will see the function GetPlayer(). You can’t just tell Papyrus “ If akTriggerRef == Player because it has no idea what “Player” is set up an Actor Property at the top of course, which does work as well). Ultimately there is no single way to HAVE to write a script to get it to work, just some ways are more efficient than others. The GetPlayer() function is not a function of ObjectReference base script, which is why Game is in front of it, this function is saying to look inside the “Game” script for the function “GetPlayer()”. Remember the line Game.GetPlayer(), you will be using it a LOT and it saves you from having to set up and define a property for the player, and is faster to process as well.
Other functions you will see and are fairly self-explanatory are GetStage(), SetStage() Disable() and these all call for a clear action, while IsCompleted() is only used during an IF check to see if it is true or not. Notice the double equal signs? (==) this means “Equals” or “IS” if you only had one equal sign instead “=” you are telling papyrus to set whatever is in front of it to the value of what is behind it, so any time you are wanting to check counts or if something is true or not, always use == and not = which is a simple oversight sometimes. Alternately <= (less than or equal to), >= (greater than or equal to) and != (not equal to) are all valid conditional check values.
Quick note on IF, ELSEIF, ELSE.
These are very common and are used extensively. The core principle to remember:
IF: if this following condition passes as defined, then proceed further into the code, if it doesn’t then the code will stop right there
ELSEIF: Always run right after an IF statement which will check a second condition ONLY if the first IF statement fails
ELSE: Another line run in the IF block if you want a default “if all else fails” scenario. Not used all the time, but can be handy
Other things to note from the Fig
All “blocks” of code are formatted to show how they flow, all events and all IF blocks start and end with a closing END statement. So EVENT defines all code within it that should occur when that event happens, then closed with an ENDEVENT statement. So a script can easily run more than one event block in it. Perfect example are item equip and unequip scripts like the Ayelid waystone script which runs a set of functions OnEquipped() and has another set of functions OnUnEquip() events.
IF Statements follow this same model:
If (Something passes as true) Then…
Do some special functions
Elseif (some other conditions passes but the IF one above doesn’t)
Do something different
Else (if ALL stuff above fails)
Do something complete different
You can have as many ElseIF statements in the IF block as you need, and each will only be checked if the ones above it fail, so you can set them all up in priority order, which is how I make the museum displays check for replica versions before checking for originals for example.
You can only have 1 Else statement because it’s the fallback action that happens if you want SOMETHING to happen when the rest of the conditions all fail. Most common is a message box that tells you you are missing something.
Well that’s the basics in a nut shell. There are many many more big points of interest and tips I have but I think using this and delving in first would be better than me regurgitating factoids that will certainly be lost without having context. Hope this all helps.