An introduction to Oblivion scripting
Scripts play a major role in Oblivion modding. They handle many things and are used for many purposes; nearly every quest has a script, every trap is controlled by a script, every innkeeper that offers room is backed by a script, some unique spells have scripts - and the list goes on.
Many people who do not have any previous programming experience have difficulties learning how to script and try to avoid it as much as possible. While many, many things can be done without a single line of code, or simply by modifying a vanilla Oblivion script, when things get specific, a script will be needed. Don’t worry, when I started modding Oblivion I hadn’t touched any scripting/programming language before, and here I am, writing a tutorial about it. :P
Imagine, you’ve made quest, and you want it to advance to the next stage when the player has found a certain item. Or perhaps you want two NPCs to have a conversation at a given moment. Without a script, that’s not going to happen.
Fortunately, learning how to script is easier than most people think. Oblivion scripting language is very straightforward and it’s easy to learn the basics. On the other hand, it also offers so many possibilities. Especially with OBSE, the Oblivion Script Extender, you can go far beyond what was previously thought to be possible. Scripts aren’t limited to boring administrative tasks. The sky is the limit. (The sky being the game engine’s limitations. ;) )
When you finish this tutorial, you should be familiar with the basics of Oblivion scripting.
It is assumed that you have at least some knowledge of the CS before you start this tutorial.
Know your working environment: the Script Editor
The Construction Set comes with a nifty tool for scripting: the Script Editor. This is your home while you follow this tutorial.
First, let’s open the Editor by clicking the pencil icon in the upper left corner.
This is what you should see:
As you can see, there are several buttons in the top left corner. Don’t be frightened by this vast amount of clickable things, you’ll only need two or three of these.
Before we continue, you may want to change the size of the Editor a bit. There’s lots of space left on your monitor, why not utilize it? It’s rather small by default, which is annoying if you’re editing large scripts. Just hover your mouse over the lower right corner till an arrow appears and drag it around to change the size like you’d normally do.
Now, back to that truckload of buttons we discussed earlier. The open and save buttons (first two), together with the new button under Script are the only ones you need, save the script type box, which we‘ll get to later.
The open button, is surprisingly, used to open scripts. Pretty straightforward, eh? Give it a try. You’ll see lots of scripts, (unless you forgot to load Oblivion.esm) which pretty much confirms the point I made earlier: scripts are used for so many things, the game wouldn’t be the same without them.
Onto the save button. Actually, save isn’t the right word. I’ve been lying to you. It’s called the “compile” button. When you compile a script, it’s processed into something the computer can understand. But don’t worry, you won’t end up with lots of 1’s and 0’s, if you open a compiled script, you’ll see it’s still perfectly readable.
The other buttons are not really important. What is important, though, is that you never click the recompile all button (the one that looks similar to the compile button, but red). If you click this, the CS will recompile every script and you’ll end up with an esp several MB’s large, and tons of conflicts with other mods that touch vanilla scripts.
Note that you must always create a new script if you don’t intend to touch the old one, even when you’re copying from another script. Merely changing the script’s name will not cause a “Do you want to create a new form” prompt.
Now, let’s create a new script so we can continue to the next part of this tutorial. Click Script -> New. You should see the background color change to white, and you’ll be able to type into it.
Before we continue, I’d like to point out that there are some useful tools such as “Find Text” under Edit. They’ll be especially useful if you’re editing large scripts consisting of hundreds of lines.
Anatomy of a script
The first thing you should do when creating a new script, is declaring the name. This is done by adding this line,
where [Name] is the name of your script. Of course, it’s hard to find a script in that huge list when you need to open something. Many people add “aaa” or “zzz” or a number as a prefix to put the script on top (or on the bottom) of the alphabetical list. They often make a large mistake though; a script’s name should never, never start with a number. This is known to cause issues. Using a number after the first character is safe.
Using aaa or zzz is fine, though may cause incompatibilities, because many people are using the same script names this way. It’s best to make sure your script name is unique: a good way to do so is by taking the first three letters of your (nick)name, and then your mod’s name (or an abbreviation of it). This is how I named the scripts for my Automatic Difficulty Increaser mod:
Scn ArgADIQuestScript;This will rule out 99.999% of conflicts
If you’ve been paying attention, you’ve noticed two odd things. Firstly, the “Scn”. This is just a short form of ScriptName. It saves you some work. Secondly, the text there. Won’t that horribly confuse yhe Editor? Yes, it would, but because I put a semicolon (;) before the text, it will be ignored when the script is compiled. The compiler ignores everything after a semicolon, as well as blank lines. This way you can put comments in your scripts. Note that if you want to put a comment on multiple lines, you’ll have to use a semicolon for every line.
This is also a good moment to say that Oblivion’s scripting language is case-insensitive. It doesn’t matter whether you use this:
Set Self to GetSelf
set self to getself
I recommend the first approach though. It helps keep your code readable, and it’s much more elegant.
After declaring the script’s name, you declare variables, which I’ll explain later.
Then, you start by adding a Begin/End block, in which you put your code. A script can have multiple Begin/End blocks.
This is how a script could look:
Scn MyQuestScript;The script’s name
Short DoOnce;Declared variables Short GoldCount
Begin GameMode;Begin the Begin/End block
;Code goes here If DoOnce == 0 Set GoldCount to Player.GetItemCount Gold001 Player.RemoveItem Gold001 GoldCount Set DoOnce to 1 EndIf
End;And close the Begin/End block
I should mention that Oblivion always processes scripts from the top to the bottom (surprise, surprise). In vanilla scripting, there’s no exception to this rule, save the “Return” function, which causes script processing to go back to the top of the script. OBSE introduces several flow control functions such as “Label” and “GoTo”, but we won’t discuss those here.
Before we continue, take a look at the Script Type box we saw earlier. Click it, and you’ll see a dropdown menu with three options: “Object”, “Quest” and “Magic Effect” . Object scripts are scripts that can be attached to an in game object, and you’ve likely come across them before. Note that they are attached to a base object, but that it will run separately on every instance of that object in game. Object scripts run once every frame, but only when the object is loaded (= the player is in the same cell). Quest scripts, also referred to as global scripts, are scripts that can be attached to a quest. They run once every five seconds (or whatever other value the game setting is set to), but that can be changed by using the magic fQuestDelayTime variable, which I’ll tell you about later. Finally, Magic Effect scripts can be attached to spells. Yeah, you can do that too! They’ll run when the spell hits someone/the spell effect kicks in.
Note that scripts will not run unless they are either:
- Attached to a quest that is running
- Attached to an object that is placed in the game world
- Attached to a spell effect that is active on an actor
Begin/End blocks form the body of your script. There are several block types. The type of a block determines when the code inside it is processed.
Begin OnEquip … End
runs once every time the item this script is attached to is equipped. As I said before, multiple Begin/End blocks can be used in a script. This allows you, for example, to process code when an item, a ring, for example, is equipped and unequipped so you can “summon” a sword when the player wears the ring.
A Begin/End block always starts with “Begin” (didn’t expect that, did you?), followed by a block type. Then you put in your code, after which you end with “End”. For example:
Player.AddItem WeapSteelLongsword 1 Player.EquipItem WeapSteelLongsword 1
Player.UnequipItem WeapSteelLongsword 1 Player.RemoveItem WeapSteelLongsword 1
Obviously, not every block type can be used in every script type. While it won’t give any errors if you try to compile it, an OnEquip block in a quest script is useless, simply because you cannot equip a quest. (If you do manage to equip a quest, I’ll buy you a beer.)
The ScriptEffectStart,ScriptEffectUpdate and ScriptEffectFinish blocks are a bit of a special case. They can only be used in magic scripts, and they will only run if the spell the script effect is applied to hits an actor. (Or, in case of a self spell, when the spell is cast.) What’s important to know is that they run on the actor they’ve hit, so calling GetSelf (I know, I haven’t told you about functions, but this has to be said now) will return the reference of the actor (or any other object, for that matter) the spell hit.
Without functions, scripts would be useless. Functions make changes to the game world and are used to check what’s going on in it.
There isn’t much I can say about this, so I’ll briefly explain the syntax of one of them, then provide you with some of the most common functions - the ones that you’ll need the most - and links to their respective CS Wiki pages.
Let’s take that example script I showed in the previous chapter. There were a few lines I haven’t discussed yet:
Player.AddItem WeapSteelLongsword 1 Player.EquipItem WeapSteelLongsword 1
Player.UnequipItem WeapSteelLongsword 1 Player.RemoveItem WeapSteelLongsword 1
AddItem, RemoveItem, EquipItem and UnequipItem - they’re all functions. They tell the game what to do. Can you guess what they do? If not, get yourself checked. When I said Oblivion’s scripting language is straightforward (well, most of the time), I was being serious.
But, if AddItem is the function, what’s all that other rubbish? That’s additional data to tell what needs to be removed from whose inventory. Nearly every function has a unique syntax. In this case:
[Reference].AddItem [Item] [Count]
[Reference] is the container (can be an actor, such as an NPC or creature, or an actual container). Note that only persistent references can be used in a script. If you need to use one, find the object you need in game, double click it, enter a reference name (which you’ll be using for [Reference]) and check “Persistent Reference”.
[Item] is the item to be added. Put the editor ID of an item, such as “WeapStealLongsword”, in here. Note that you must use the editor ID, not the name. This:
Player.AddItem Steel Longsword 1
will not work. [Count] represents the amount of the item that should be added.
Of course, every function has a different syntax. This is only an example.
I should also mention that there are two types of functions: active functions, and passive functions. Active functions make a change to the game world, such as adding an item to a container.AddItem is an active function. Passive functions check certain states and return the appropriate value.GetActorValue is an example of a passive function.
Now, as promised, a list of some of the function you are most likely to need as a novice scripter. Be sure to have a look at the List of Functions at the CS Wiki as well. There are hundreds of them, and nearly every one is documented there.
And links to their respective CS Wiki pages:
If statements are the backbone of a script. They allow you to process certain pieces of code by making logical comparisons.
An if statement is used to check if a condition is true, and then act to it accordingly. It always starts with “If”, then a comparison (which consists out of an argument, a comparison operator, and a second argument), then the code that is processed if the comparison is true, then the statement is closed with “EndIf”.
For example, in a human readable way: “If the player has 10 or more gold pieces, do this.”, or in code:
If Player.GetItemCount Gold001 >= 10 Message "You have ten or more gold pieces. Now you can buy those fancy shoes you want." EndIf
First, we check if the comparison is true, in this case when the player has ten or more gold pieces. ("Player.GetItemCount Gold001" is the first argument, ">=" is the comparison operator and "10" is the second argument.) If the statement is true, i.e. the player has ten or more gold pieces, the code between If/EndIf will be processed.
GetItemCount is one of the passive functions mentioned earlier. This one returns the amount of gold the player currently has, so if the player has 23 gold pieces, this is what happens during runtime:
If 23 >= 10 Message "23 is obviously greater than or equal to 10." EndIf
Obviously, 23 is greater than 10. Therefore, this returns true and the code within the if statement is processed.
To make these comparisons, comparison operators are used. There are several of them, listed below:
Of course, for the previous example we could have also done this:
If 10 <= Player.GetItemCount Gold001 Message "Ten is less than or equal to the amount of gold you have. Also, I heard that Alessia Caro bought the same pair of shoes. Awkward!" EndIf
If the player still has 23 gold, this would be the same as:
If 10 <= 23 Message "Ten is less than or equal to the amount of gold you have." EndIf
Of course, you shouldn’t swap GetItemCount with 23 in your code. After all, we want to know if the player has enough money, not if 23 is greater than or equal to 10. ;)
ElseIf can be used to check another comparison if the first comparison is false:
If Player.GetItemCount Gold001 > 10 Message “You have more than ten gold pieces.” ElseIf Player.GetItemCount Gold 001 == 1 Message “You have exactly one gold piece.” ElseIf Player.GetItemCount Gold 001 == 2 Message “You have exactly two gold pieces.” EndIf
(In case you haven’t checked out the “message” function, it shows a message in the top left corner of the screen. Again with the straightforwardness!)
If the first comparison is false, the second comparison is evaluated. If it’s true, the code between the comparison and the next ElseIf/Else/EndIf is processed and the rest of the comparison in the if statement are skipped. If it’s not true, the next ElseIf comparison is evaluated and so on. There’s no limit to how many ElseIfs you can use.
There’s also “Else”, which should be used only at the end of an if block:
If Player.GetItemCount Gold001 > 10 Message “You have more than ten gold pieces.” ElseIf Player.GetItemCount Gold 001 == 1 Message “You have exactly one gold piece.” ElseIf Player.GetItemCount Gold 001 == 2 Message “You have exactly two gold pieces.” Else Message “I don’t know how much gold you have.” EndIf
The Else part is processed if none of the other comparisons were true. Naturally, an if statement can only have one “Else”.
There is no limit to the amount of if statements in a script.
Then, there are the logical operators, which can be used to “combine” comparisons in one if statement:
If Player.GetItemCount Gold001 >= 10 && Player.GetLevel > 5 Message "You have ten or more gold pieces, and your level is greater than five." EndIf
This bit of code uses the "&&" operator to check if both of these comparisons are true. Again, in a human readable form: "Does the player have ten or more gold pieces, and is his level greater than five? If so, show a message that says...". The two comparisons are combined into one if statement. With the && operator, both comparisons on either side must be true for the code in the if to be run. So, if the players level is 3 and he has 6 coins, it won't run. But, if he has 15 coins and his level is 3, it won't be run either because only one of the comparisons is true.
“&&” is called a logical AND. An alternative for this would be a nested if statement:
If Player.GetItemCoun Gold001 >= 10 If Player.Get Level > 5 Message "You have ten or more gold pieces, and your level is greater than five." EndIf EndIf
A nested if statement is an if statement inside an if statement. Because the code inside an if statement only runs if the comparison is true, the message will only be displayed if both if statements are true. (The second if is inside the first if, so the second if will only be evaluated if the first if is true. Thus, by putting the message in the second if, both must be true in order for it to be displayed.) Oblivion allows a maximum of ten nested if statements.
“||”, is a called logical OR. In the following example, the code within will be processed if one of the two comparisons is true:
If Player.GetItemCoun Gold001 >= 10 || Player.Get Level > 5 Message "You have ten or more gold pieces, or your level is greater than five." EndIf
The || operator will be true if one of the two comparisons is true, but also if both are true. So if the player has 12 gold pieces and his level is 1, the code within the if will be run. If the player has 12 gold pieces and his level is 7, it will also run. Only when the player has less than ten gold pieces and his level is not greater than five will the if be false.
The logic behind the logical operators:
X and Y represent comparisons that are either true (1) or false (0). You can see what I explained
You can use multiple logical operators and comparisons on a single line:
If Player.GetItemCoun Gold001 >= 10 || Player.Get Level > 5 && x == 4
Note that in Oblivion, || is evaluated before &&, so technically, this is what happens:
If Player.GetItemCount Gold001 >= 10 || Player.Get Level > 5 && x == 4 If ( 0 || 1 ) && 0 If 1 && 0
If Player.GetItemCoun Gold001 >= 10 || Player.Get Level > 5 && x == 4 If 0 || ( 1 && 0 ) If 0 || 0
What happened in the first example is this: "Does the player have ten or more gold pieces, or is his level greater than 5?" Answer: true, because his level is greater than five. Then, the &&: "Is my previous question true, and does x equal 4?" Answer: false, because, even though the previous question was true, x does not equal 4.
Parentheses, “(“ and “ )”, can be used to override this precedence, like in math. The part within parentheses is evaluated first. It's important to keep that in mind when you use if statements with both logical ANDs and logical ORs. For example, if you want to make sure the players level is greater than 5 and that he has more than 10 gold pieces before you give him a sword, for example, you'd do this:
If Player.GetItemCount Gold001 > 10 && Player.GetLevel > 5 Player.AddItem WeapSteelLongsword 1 EndIf
But later, you decide that it isn't fair for players who are level five and under, so you give them an alternative way to get the sword: they need a black soulgem. But if you do this:
If Player.GetItemCount Gold001 > 10 && Player.GetLevel > 5 || Player.GetItemCount BlackSoulGem >= 1 Player.AddItem WeapSteelLongsword 1 EndIf
This will happen, because "Player.GetLevel > 5 || Player.GetItemCount BlackSoulGem >= 1" is evaluated first: "Is the players level greater than five, or does he have one or more black soulgems?" "Does the player have more than ten gold pieces, and is my previous question true?"
as opposed to what you want to happen:
"Does the player have more than ten gold pieces and is the player's level greater than five?" "Is my previous question true, or does he have the alternative, a black soulgem?"
To fix the problem:
If ( Player.GetItemCount Gold001 > 10 && Player.GetLevel > 5 ) || Player.GetItemCount BlackSoulGem >= 1 Player.AddItem WeapSteelLongsword 1 EndIf
We've used parentheses to override the normal precedence rules. Now, the part within the parentheses if evaluated first.
In Oblivion scripting, the parentheses are only needed if the default precedence rules need to be overridden. It’s better not to use them otherwise.
Get it? If not, no need to worry. You'll get it eventually. ;)
Variables are used to store data for later use. They are stored in your saves, so you can even access them after restarting the game engine. (Note that that does not apply to all script types, but that's not important right now.) Variables are often used in conjunction with if statements to control the flow of a script, but they are also used in calculations or simply to save something, such as, for example, how much money the player has on his bank account, that needs to be accessible at all times.
Oblivion has four types of variables:
Long and short variables are used to store integers, i.e. whole numbers such as “5” or “34”. Float variables are used to store floating point values, i.e. numbers with decimals such as “6.8264”.
You’re not likely to find a reference variable outside Oblivion scripting (in this context, at least), because reference variables refer to an instance of an object in the game. “PlayerRef”, which refers to the player, is one of them. Note that in Oblivion scripting, many people (even Bethesda itself!) use “Player” instead of “PlayerRef”. That’s wrong. If you use “Player” with a function such as GetItemCount, you’re actually referring or the player base object, not the player reference which is the one that’s running around in the game. Fortunately, the compiler will notice this mistake in most cases and swap it with PlayerRef in the compiled code. Using “Player” usually gives no problems, but if you’re doing complicated things such as checking for the player ref in an if statement, things can go wrong. It’s a good idea to use “PlayerRef” if you’re doing complicated things. “Player” will be fine with (virtually) every reference requiring functions though. I should also mention that a ref variable can also refer to a form ID/base object.
Now, let’s continue. Before you can use a variable, you must declare it:
Short MyVariableName Short AnotherVariable Ref RefVariable Float Var1234
As you can see, variables need to be declared before the first Begin/End block, i.e. right after the scripts name. First, you need to declare the variable type (either “Short”, “Long”, “Float” or “Ref”), hen you can enter a name. You can name it whatever you want, but you can’t use spaces or other weird characters, just letters and numbers. E.g., this:
Float Door Timer Short My&var
is wrong and won’t compile.
Declared variables are NULL, “0”, by default. So this:
Short DoOnce … If DoOnce == 1 ;Something EndIf
will never work because the variable is equal to 0, not 1. This example also shows that you can use variables in if statements just like regular numbers or passive functions. Another example of that:
Short Var1 Short Var2 … If Var1 == Player.GetItemCount Gold001 ;Do something EndIf
If Var1 > Var2 ;Get it? EndIf
Now obviously, I Haven’t told you the most important thing: how to assign a value to a variable. It’s really easy, just use “Set”:
Short Number Float FoatingPoint Ref MyBestFriend … Set Number to 5 Set FloatingPoint to 8.436 Set MyBestFriend to JauffreRef
The Set statement always follows the same syntax: “Set [Variable] to [Value]”. You can also set a variable’s value to the value of another variable, or store the value a passive function returns to a variable:
Float Var1 Float Var2 Short Count … Set Var1 to Var2 Set Count to Player.GetItemCount Gold001
There are a few things you need to keep in mind though. Firstly, function return a certain kind of value. GetPos, for example, returns a float while GetSelf returns a ref. If you want to know which variable to use with a function, have a look at its CS Wiki article.
Secondly, if you assign the value of a floating point variable to a short integer, it will be truncated, i.e. the decimals will be thrown away. Therefore:
Short Var1 Float Var2 … Set Var2 to 2.55 Set Var1 to Var2 Message “Var2 is %.2f” Var2
will print “Var2 is 2.00”. Yes, you can print a variable by putting %.2f or %g between the quotes, then the variables’ names in the right order.
Message “Var1 equals %g and Var2 equals %g” Var1, Var2
You can print multiple variables in at a time, as shown above. Also, if you use %.2f, the .2 determines how many decimals are shown. For more on that, have a look at the CS Wiki article on Message.
And now, the final thing I’ll be teaching you in this tutorial, and the last thing you need to get a grasp of the basics of Oblivion scripting: arithmetic operators and math.
Oblivion uses the following arithmetic operators:
These operators can be used to perform simple mathematic operations. They can be used in variable assignments:
Set SomeVar to SomeOtherVar * 2 Set SomeVar to Player.Get Level + 5
but also in if statements:
If ( Player.GetItemCount Gold 001 - 50 ) > 0
Normal precedence rules apply to the arithmetic operators. * and / Are evaluated first, then + and -. Here, you can also override the precedence rules by using parentheses:
Set Var1 to 37 + 3 * 2;Equals 43 Set Var1 to ( 37 + 3 ) * 2;Equals 80
The Oblivion Script Extender, adds, besides tons of awesome functions, many new variable types, such as array and string variables, and arithmetic operators, such as ^ (exponentiation) and % (modulo). While you won’t be needing these until you’re making very complex scripts, they’re extremely useful. Arrays, for example, allow you to acquire more space to store data during runtime, without having to declare hundreds of variables and hoping that you won’t run out of them. Once you get more familiar with scripting, OBSE is definitely worth checking out.
There, that’s it. You are now familiar with the basics of Oblivion scripting. (If I do my job right, that is.) If you actually read all of that, kudos, and if you actually think you have a better understanding of scripting, more kudos. Still, this here barely scratches the surface. There is so much to scripting that it can’t all be explained in a tutorial. You’ll have to experience it for yourself.
At this point, you may want to have a look at the tutorials the CS Wiki has to offer. They’re all a bit specific, but you may be able to pick up a thing or two nonetheless. What I recommend though, is that you open up a few mods you’ve downloaded and dissect the scripts. You’ll learn a lot that way.
Of course, the only way to master a programming language is by practice. You should now be able to string together a basic script, so fire up the CS and write some scripts from scratch. It may be hard at first, but you’ll get the hang of it in very quickly, trust me.
Furthermore, if you have any questions or suggestions, feel free to either comment here, or to send me a private message. If you run into a problem, you can also post a request for a little help at CS support sections at The Nexus Forums or the Bethesda Game Studios Forums.