Celowin - Part II: Local Variables

Preamble


The purpose of this sequence of lessons is to take a complete beginner to programming, and teach him or her how to use NWNScript to write modules. The early lessons will be very basic, and anyone that has done any coding at all will be able to skip over them. The goal here is to make the lessons so that even the people that just shudder at any type of code can learn.


Feel free to post these lessons on any forum, print them out, or modify them. However, just give me credit for doing them.


Any comments on these lessons, good or bad, can be sent to me, Celowin.


I am going to assume that anyone looking at these lessons has at least played around with the Aurora Toolset a bit. If there is enough feedback that people don't know how to do the simple placements that I have in these lessons, I will consider spelling out in more detail what needs to be done.


Introduction


This lesson will likely be a bit longer than the first one, and deal with some more advanced concepts. I'm not certain how well I'll be explaining things here, so please ask questions if you don't understand something. Odds are if what I write isn't clear to you, there are five other people that want to know the same thing.


Let's just modify the module we started before. Open it up in the editor.


Right click on the NPC we have, go to properties, and go to the script tab. (We could make a new NPC, but do we really want this one chanting his song in the background? It gets old really quickly....)


We're going to need two scripts this time, both short.


The first one goes into the OnSpawn handle.


void main()
{
  SetLocalInt(OBJECT_SELF, "SINGER_COUNT", 0);
}

Save it as tm_singer_os ('os' for OnSpawn)


Now, even though this is only one line, it is things like this that tend to freak out people that have never scripted before. So, I'm going to explain this one before I go on to the "real" script.


First off, note that we're putting this on into a different handle than before. Any script attached to OnSpawn is run exactly once... when the NPC first loads into the game. Because of this, it is ideal for initializing things, which is what we are doing here.


This one line sets up a variable. Think back to your last math course, what was a variable? A letter which represents a number. That is pretty much the same thing that we are doing here, except that instead of using a single letter, we give it a longer name.


The command SetLocalInt is the function that is setting everything up. It takes in three inputs, separated by commas.


At this point, I hope the second and third of these make sense... we are calling our variable SINGER_COUNT and we are giving it the value of 0. It is the first input that takes a bit more explanation.


Nearly everything in a module is an object. NPCs are objects. Placeables are objects. Waypoints are objects. Even the PCs themselves are objects. With so many objects floating around in a module, the tricky part becomes making sure you are referring to the right one.


OBJECT_SELF is one of the most handy ways to refer to an object. As you might guess, it is used to refer to whatever object is calling it... in this case, our script was attached to an NPC, so that NPC is what is meant by OBJECT_SELF


Putting all this together... this one line has defined a variable called SINGER_COUNT, given the variable a value of 0, and stored it with the NPC SINGER.


Also of note is that the 'Int' in SetLocalInt stands for "Integer", or basically a whole number. We could set it to 4, or 0, or –35, but we couldn't set it to 3.8, for example.


Now, let's go on to the second script. We'll overwrite the OnHeartbeat script from the last time.


int nCount=GetLocalInt(OBJECT_SELF, "SINGER_COUNT");
void main()
{
    nCount = nCount+1;
    ActionSpeakString("I have spoken "+IntToString(nCount)+" times.");
    SetLocalInt(OBJECT_SELF, "SINGER_COUNT", nCount);
}

Save it, keeping the name tm_singer_hb


Before I explain this, it is probably a good idea to take a look at what it actually does. So close your script window, click OK on your NPC, and save the module. Start up the test module, and see how the NPC behaves.


This is the first script I have done where we have had anything before the void main(), so it is worth mentioning. Everything before that is called the "initialization" of the script. Basically, it sets up things that the script can refer to. Unlike the local variable that we stored before, this nCount only stays around as long as the script is running. As soon as the script finishes, it gets rid of nCount. For this reason, the variables created in a script like this are called "temporary variables."


Notice how many times we used nCount inside the body of main. Imagine how long and confusing the script would be if every time we had to write something like GetLocalInt(OBJECT_SELF, "SINGER_COUNT") instead of just nCount. Pretty sick, eh?


Now, the name nCount is another bit of standardization. Any time you declare a temporary variable, the first letter is a tag to point out what kind of value it takes. The 'n' stands for integer. According to the code, you could give it any name you want, but by naming it this way, you know immediately that it must take an integer value. The 'Count' part of the name tells you a bit about what it will be used for.


The int says we are defining an integer variable. The '=' means "give it the value of." And the GetLocalInt retrieves the value of the variable SINGER_COUNT. Note the similarity of the functions... we used SetLocalInt to set up and store the local variable, and GetLocalInt to retrieve the value.


So, that very first line does a lot. It creates a temporary variable nCount that the script can use, and right away gives it the value of the variable assigned to SINGER_COUNT and stored on our NPC. So, the first time this is called, nCount has a value of 0, since that is what we initialized it to in our OnSpawn script.


I know, this is a lot of explanation for one line of code, and I doubt anyone would digest it all on one pass. Read through it again, and if it still isn't clear, don't worry too much about it. This kind of thing makes a lot more sense once you've done a few examples.


Let's move on to the main body of the script, then. Consider the first line:


nCount = nCount+1;


This just takes our temporary variable, and increases it by one. For expressions like this, it helps some people to read the word "set" in front of the expression. So in words, this might be read "set the variable nCount equal to the value of nCount plus one."


If nCount starts out with a value of 9, this will set it to have a new value of 10.


(As an aside, another way this can be put into NWScript is to write the line as


nCount++;


The '++' stands for "increment", that is, increase by one. This is fine once you are used to it, but is generally a bit more confusing for people that are just learning.)


The second line is an ActionSpeakString, but the inside is kind of funny. I've done a few things here... first, notice that we have three things on the inside, separated with + signs. When applied to strings (text), the + just adds one string on to the other. So for a simple example: "This is a string." would be exactly the same as "This is "+"a string."


There would be no reason to do something like in the above line. But it comes in handy when we introduce the middle part of the expression: IntToString(nCount). This does exactly what it says... nCount is an integer. This changes the numerical value that it has into a string, or bit of text.


This idea can be a bit confusing for a non-coder, so let me try to make an analogy. Suppose I say something silly like "an elephant wearing a tutu." You immediately get a picture in your head, and don't think about the individual letters that go into making up the phrase I said. It is only when you go to write it out that you start thinking a-n-space-e-l-e-p- and so on.


The computer is the same way. It is storing the value of the variable, and it isn't thinking about how to "write it out." The IntToString tells it to do that conversion to text so that the NPC can speak it.


The final line you should understand... it just stores the new value of nCount back into our local variable. Remember that the actual nCount is only temporary, and will be discarded once the script ends. So if we want the variable to update, we need to store it again.


Preview of Lesson Three


Lesson two was originally going to be two or three times as long, but I think I'm going to split it up into separate lessons. However, I just can't leave our last script in the state it is in... it has an error that just irks me.


If you are detail oriented, you might have cringed a bit when you first tested the script. The very first words out of the NPC were "I have spoken 1 times." Easy enough to understand what is meant, but not the height of grammar, either.

The way to get around this is by using a "conditional" or "if-statement". I'm not going to explain this all until Lesson Three, but here is a script that fixes the problem. Just replace the heartbeat script with this.


int nCount=GetLocalInt(OBJECT_SELF, "SINGER_COUNT");
void main()
{
  nCount = nCount + 1;
  if (nCount == 1)
  {
    ActionSpeakString("This is the first time I have spoken.");
  }
  else
  {
    ActionSpeakString("I have spoken " + IntToString(nCount) + " times.");
  }
  SetLocalInt(OBJECT_SELF, "SINGER_COUNT", nCount);
}

Hopefully, even without explanation, this is somewhat easy to understand. And really, once you fully understand it, you are probably 80% of the way to writing your own scripts.





 author: Celowin, editor: Iskander Merriman