Celowin's Scripting Tutorial, Lesson One - The
Basics
Introduction
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 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.
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.
Let's Begin
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 menu that
comes up.
- 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 "On
Heartbeat" slot.
- Type in the following script (Every
character needs to be exactly the same.):
NWScript:
void main(){ClearAllActions();
ActionSpeakString("This is the song that never ends.");
ActionWait(1.5);
ActionSpeakString("Yes it goes on and on, my friends.");
ActionWait(1.5);
ActionSpeakString("Some people started singing it not knowing what it was.");
ActionWait(1.5);
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 "On
Heartbeat" 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 "On Heartbeat" 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 "On Heartbeat" 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.
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 ), even without any inputs.
What about the { and }?
These are used to set off sections of the
script. In this case, it is used to say that
everything we have written is part of our
"main".
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 "On Heartbeat" 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. The 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 the English
language, 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 the 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 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
heartbeat 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.