Celowin - Part IV: User Defined Events

Preamble


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. 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.


Clean Up on Lesson Three


The more astute people following this sequence of lessons noticed that the guard script we came up with in Lesson Three was "good enough", but didn't behave exactly as we wanted it to. Every time the guard was "friendly", it would call out the greeting twice. Only people paying close attention would notice, but it is something that we should probably fix before going on. Besides, it gives me a chance to explain another concept.


First, we need to pinpoint why the guard was behaving that way... and it boils down to the OnPerception script handle. There are four things that can cause the attached script to fire:

So, what was happening is that the NPC was both seeing and hearing the PC, and so the script was being executed twice. Not the end of the world, but not the behavior we wanted, either.


Let's fix this in the following way: We'll have the guard only react if it sees something. After all, the guard is looking for a ring, and those are usually pretty tough to hear.


We could do this by nesting another level of if statements, but it was already getting a bit confusing with all the different layers we had. Instead, what we want is for the script to check two things at once: we want to be sure the object perceived was a PC and that it was perceived by sight. Only if both things are true will we do all the rest.


We can do this just by modifying the condition, along with the operator '&&' (read as simply 'and'). When put into a condition, '&&' is a way of linking two conditions together. The entire thing will be true only if both parts are true. To fix our script, then, all we have to do is change the line:

if (GetIsPC(oSeen))

to

if (GetIsPC(oSeen) && GetLastPerceptionSeen())

Then, the script will only run if it was a PC noticed by the guard, and also the guard saw the PC, instead of noticing the PC some other way.


The GetLastPerceptionSeen is a little function that returns TRUE if the last perception was by sight, FALSE if any of the other three.


I won't do an example right now, but another way of linking conditions together, instead of '&&' is '||'. '||' is read "or" and means that the condition is true if either one of the parts is true. This might be used if there were multiple reasons why the guard might attack. (Perhaps guard would attack if the PC didn't have the ring, or if the PC was carrying the head of the mayor. Either one by itself is a reason for the guard to attack.)



A Confession


Right now, I have to admit that I've been lying to you all throughout the past three lessons. Multiple times, I've said that I use the methodology I do is because I am doing everything the way I would inside a real module. But now I have to come clean – not a single script I have done here is really the way I would put it in final form.


The problem is, that our NPCs so far have been totally unresponsive to most stimuli. The guard we finished up top is starting to get there, but I would hardly call its behavior realistic. If you're interested, here is an experiment you could do: Beef up the HP and level of the guard. Start up the module. Get the ring, so the guard will be friendly to you. Then go attack the guard. It will just stand there, and let you beat on it. This is definitely not what we want from most of our NPCs.


The reason that we have been creating such morons is that we threw away all of BioWare's hard work in writing scripts. The default scripts that we have been deleting define a number of useful "standard" behaviors that we really probably want to keep in almost every instance. (Actually, one of the NPCs we'll be creating today we will want to throw away the default scripts, but more on that when we get to it.)


So, how do we define our own behavior, without throwing away all the default stuff? We use the "user defined" script handle. If we are clever, we can really use every other handle – we'll make only minor modifications to the OnSpawn script, and do most of our scripting in the OnUserDefined script.


We've spent so much time on our guard so far, let's go ahead and fix it up to the way it should be.

Now, if we start up the module, the guard will behave more realistically. He still does the "friend or foe" reaction that we scripted into him, but also reacts to other stimuli. If you attack him, he fights back. There are lots of other behaviors that are scripted in there, many of which happen "behind the scenes" that you will probably never notice.


So, what did we actually do? Well, when removing the '//' in the OnSpawn script, we "uncommented" something. Remember that anything after '//' on a line in a script is ignored. So what BioWare did was put in a bunch of "optional" stuff into the OnSpawn script, and put '//' in front of it so that it wouldn't happen.


But now, we want some of it to happen. By removing the '//', now we are saying that we want that line to actually take effect.


So, what does that line we "put back in" actually do? In essence, it is saying: "When you are running the OnPerception script, also do what I put into the OnUserDefined script."


Now, the more astute of you might be thinking ahead, and asking "What if I want to have multiple new behaviors from an NPC? What if I want to have it do something special on OnPerception and also on OnHeartbeat?"


It can still be done, but takes a bit of extra work. Just to keep the script small, let's make an NPC that does something simple. It will say "I'm bored," every six seconds, and bow when it sees a PC.



Question and Answer time....


What is this GetUserDefinedEventNumber?

This is exactly the number I told you to pay attention to up there. BioWare was very clever... not only can each different handle call the user defined script, but each one passes a different number to it when it does so. So, what I'm doing in that first line is checking which one of the scripts called this one. Was it the Heartbeat (1001) or the Perception (1002)?

What about this switch command?

I don't want to go a whole lot into detail on this one. Basically, you give it an input of an integer, and the script then "jumps" to the line marked with "case" and that number. So if our nCalledBy is 1001, the script looks down for the line "case 1001:"

It starts doing commands at that point, until it gets to a break.

A few notes on formatting: The switch command only looks within the lines tied to it with { and }. Also, like the if statement, there is no semicolon after the switch line.

When I'm writing OnUserDefined events, I usually try to always set them up with a switch like this, even if I only plan on having one type.... just because I might change my mind later. It is better to be prepared for a possibility, than have to monkey with a bunch of work because you were too lazy to plan ahead.

Hey, you didn't use { and } with your if statement!

If there is only one command attached to the if statement, the curly braces aren't needed. To me, sometimes it looks better with them, and sometimes without them. I go with whichever makes the script look cleaner. In this case, I decided that they would just clutter up the script.

One More Example


At this point, you know a good deal about scripting. I shouldn't be calling these Absolute Beginner lessons any more, because you aren't. There are still a lot of functions you need to learn, and a few more tricks. But really, there are lots of cool things that we can do with what we've learned so far, if we put the pieces together.


I'm going to do an example like that now. The script is pretty complicated, requires a lot of setup, and uses some new commands. But I think the end result is worth it.

Well, this is a really complicated script. I'm not going to explain absolutely everything about it, but I'll point out a few key things.


I'm using the local variable DARTSTATE to make sure that we don't attack when not ready, or go through the "get darts" sequence multiple times.


The ActionDoCommand is amazingly handy. It takes a non-queued command, and turns it into a queued one. Normally, as soon as the script sees a SetLocalInt instruction, it sets the local variable. This forces the script to wait until the NPC has completed all the previous actions.


The CreateItemOnObject is what is used to make the new darts for the NPC. "nw_wthdt001" is the blueprint ref for a dart, and the final 3 is the stack size.


Other than that, see if you can trace through the script yourself. There are other new commands, but most of the names are pretty self explanatory. As always, ask questions if you can't figure it out.





 author: Celowin, editor: Charles Feduke, additional contributor(s): Jenn Jimerson, Sergio Paschoal