Celowin - Part I: The Basics

From NWN Lexicon
Jump to: navigation, search


The purpose of this sequence of lessons is to take a complete beginner to programming, and teach him or her how to use NWScript to write modules. The early lessons will be very basic, and anyone that has done any programming 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 programming 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.


Open the toolset, and create a new module using the wizard... we'll call it Test Module. Create a new area, call it Test Area 001. Use whatever size and tileset appeals to you, for the purposes of this lesson it really doesn't matter. (You probably want to keep it small in size, for ease of finding things. I suggest 2x2.)

Use the "paint creatures" tool to lay down an NPC. Give it whatever appearance you wish. Now, do all the following:

  • Right-click on the NPC you just put down.
  • Click on 'Properties' from the context menu that appears.
  • Change the tag of the NPC to 'SINGER'
  • Go to the 'Scripts' tab.
  • There are default scripts in every slot. Click on each slot, and delete the script name.
  • Click on the 'Edit' button next to the 'OnHeartbeat' slot.
  • Type in the following script (Every character needs to be exactly the same.):
void main()
    ActionSpeakString("This is the song that never ends.");
    ActionSpeakString("Yes it goes on and on, my friends.");
    ActionSpeakString("Some people started singing it not knowing what it was.");
    ActionSpeakString("And now they'll keep on singing it forever, just because...");

I recommend you actually type it in instead of cutting and pasting. You will learn more about the way the script is written. If you make a mistake, so much the better.... you will see how important every character can be.

  • Click on 'Save As' and call it 'tm_singer_hb'
  • Check the window at the bottom of the screen. It should say something like: 0 Errors. 'tm_singer_hb' Compiled successfully. If it doesn't, you made a mistake typing. Double-check yourself.
  • Close the script edit window.
  • Click 'OK' on your NPC window.
  • Save your module.
  • Call up the game, and go to 'Other Modules' to start up what you've done.
  • Your NPC should be singing the little ditty over and over and over...

Analyzing It

Now for the hard part... I'm going to go step by step through what we did, and try to explain everything. I'll do this in a 'question and answer' format, trying to guess at everything someone might ask.

Why did you call it Test Module?
Really, in this case, there is no reason for it, aside from making it something easily recognized when you go to the Other Modules screen.
Why did you call it Test Area 001? Wasn't the default name the toolset assigned good enough?
For our little test module, it probably doesn't matter. However, it is a good habit to get into to rename your areas, for purposes of exporting. If you want to take just one area out of your module, and put it into a different one, it has to have a unique name. If everyone uses Area 001 in their module, then importing and exporting areas is problematic.
Why did you give the NPC the tag SINGER? Why all capitals?
For a few things, the tag needs to be all capitals. You may never run into this, but it is again, a good habit to get into.Also, I recommend giving every creature you paint a simple, easy to remember tag. SINGER describes the NPC perfectly, so it is easy to refer to him in a more complicated script.You also want to keep your tags short. I recommend 8 characters maximum.
Why did we delete out all the default scripts? Why do they show up there if we are just going to delete them?
The default scripts define a number of behaviors that we don't want to deal with quite yet. For example, depending on what NPC you originally painted, it may well have ended up attacking you when you entered the module. We deleted the other scripts just to keep things simple – we want our NPC to sing the song, and nothing else.In later lessons, I'll go into making better use of the default scripts.
Why did we put our script in the 'OnHeartbeat' slot? Why are there so many slots anyway?
Each one of these slots for scripts defines a time when that particular script is called. The 'OnHeartbeat' slot calls the script every six seconds. This is why the NPC sings the song over and over... every six seconds, he sings his four lines.There are uses for every one of the script slots, depending on the behavior you want. In fact, the 'OnHeartbeat' really should be your least used script slot. Too many scripts firing every six seconds can cause performance issues with your computer.
What is this void main()?
This looks rather simple, and the fact that it shows up exactly like this in nearly every script causes many people to just blow it off. I'm going to attempt to explain it fully here, but I can't guarantee it will make complete sense until later lessons.NWScript is coded using functions, which tell the program what to do. Some functions are pre-written for us, and we just put them into our code. However, when we are making scripts, we are in fact writing our own functions. This line is sort of 'setting up' our function, and is sometimes called the function's 'signature'.First the void. This tells the script what kind of 'answer' will come out of the function. Our little singer is performing actions, but isn't calculating out any sort of an answer. So the void says that the function doesn't actually give an answer.The main says that it is the main part of our script. We can write other functions into our script, but the main one is the part that is called when the script is started.In between the '(' and the ')' is what kind of input is coming into our script. Again, our script doesn't need any input, so there is nothing between them. We always need the '(' and ')', though, even without any inputs.
What about the '{' and '}'?
These are used to group sections of the script together. In this case, they are used to say that everything we have written is part of our main function.
Why does every line inside the main end with ';'?
Basically, to tell the script that it is the end of the line. Some complex instructions won't fit well on one line. Breaking it up onto separate lines is fine, the program just treats it as one line until it comes to the semicolon.You can think of it very much like the period in the English language. (The reason the semicolon is used instead of a period is to prevent confusion with decimal points.)
What does the ClearAllActions() do?
This is a tricky one, because in this case it actually doesn't do anything. It is put in more as insurance, hoping that it never actually has a job to perform.Again, I'll try to explain... every other line in the script is an Action command. The NPC doing the actions will do them one at a time, making sure to finish one before it goes on to the next.But now remember that our function is assigned to the 'OnHeartbeat' and will be called every six seconds. We have seven actions that we want the NPC to do, what if he only finishes 4 of them in the six seconds? There are still 3 actions left to perform, and then the NPC is told to do another 7 actions... so he has 10 things to do now. He does another 4, and then a new set of 7 instructions comes in... 13 to do now. He will fall further and further behind. Eventually, this will cause problems.There are better ways of dealing with this. But for now, it is easiest just to tell the NPC to "forget about everything you still had to do." This is what the ClearAllActions() is for.If you want to experiment, you edit the script, and change the number inside all the ActionWait() commands from 1.5 to 3.0. You'll see that the NPC can't finish the whole song before he starts it back up again.While I'm here, let's tie this back with a bit we were discussing a question or two back. ClearAllActions() is a function, just like main. So once again, having nothing between the ( and ) means there is no input to this function.
What about all these other lines in main?
These are all the instructions telling the NPC what to do. I think these are pretty obvious, but I'll try to explain them a bit anyway.ActionSpeakString() has the NPC say something. Again, we have a function, but this one actually does take an input... the text that the NPC is going to say. This text is called a "string", and similar to spoken text when written in the English language, it needs to be encapsulated by quotation marks.ActionWait() has the NPC sit and do nothing for a number of seconds equal to the number put into it. So ActionWait(1.5) has the NPC wait for 1.5 seconds between each thing that is spoken.
Why name the script tm_singer_hb?
Again, you could call it just about anything, but you want to call it something easy to remember. Standardization is the key:
  • We use 'tm' to stand for "Test Module." Any script written for the test module we will start with this two-letter code.
  • 'singer' is of course the NPC we are attaching the script to. Even though the NPC tag was all capitals, we use all lower case letters for script names. (Note: this is why I recommend using tags for objects of 8 characters or less... otherwise these script names can get very long.)
  • 'hb' says that this script is for the OnHeartbeat event of the NPC.
Why isn't this lesson any longer?
I fully realize that with just this first lesson, you can't yet do a whole lot of scripting on your own. However, I'd rather do too little than too much, and have people get frustrated with the amount of information they need to absorb. Give me a couple of days, and maybe I'll get another lesson cranked out.


Next Article
Part II: Local Variables

author: Celowin, editor: Iskander Merriman