GAME CONCEPT AND DESIGN
COSC 320.101_SU09
SUMMER 2009
Tracking Variables
One of the things you'll find that you need to do is track variables. Has the player talked to a particular NPC? Has he read a book that he needs to read before he can claim to have "learned" the recipe? ...and so forth. The only real way to do that is by setting variables and flags so that you can check them later.
This can be problematical because, as I mentioned last time, all the scripts are local scripts and have no real knowledge of any other scripts. And in a multiplayer game, you could have different players doing different things--all at the same time--causing scripts to fire in all sorts of orders. So, let's look at a way to deal with variables in order to allow yourself the ability to keep track of what the players have done.
Local Variables
What we're going to do is "attach" a variable to a player. Once it's attached, it stays attached even after the script that set it stops running. So you can set the variable in one script and then--a long time later--another script can check that variable and allow you to decide what to do depending on the result that you find. Do note, however, that if the player logs off or goes link-dead then all your custom variable attachments are lost. (There are ways and there are ways to get around this...and I'll touch on them...but basically consider them lost.)
So, the two commands we're going to look at to do this is SetLocal<type> and GetLocal<type>.
By the way, when I'm teaching programming, you'll often see me put words in angle-brackets like that. That means "something goes here, but it'll be whatever you determine." For instance, if we're creating an object I'll say: CreateObject(OBJECT_TYPE_PLACEABLE, "<your resref>" .....); Clearly, you wouldn't actually type in <your resref>...instead you would substitute your data in that place.
Ok..back to the commands. Remember when I talked about variables and how they're data-typed as int, float, and so forth? Well, when you attach a variable to an object, you have to tell NWN what type of variable you're attaching. So the actual commands are:
- SetLocalInt to attach an integer.
- SetLocalFloat to attach a float.
- SetLocalLocation to attach a location variable
- SetLocalObject to attach a reference to an object
- SetLocalString to attach a string.
Makes sense, eh? To fully use the command you have to then specify the object you're attaching the variable to, the "name" of the variable, and the value you want to set it to.
So let's assume that you've got a quest that has 5 steps that the player must complete in order. You'd then want to keep track of which step she's on. So let's say she's just talked to an NPC that sets up the quest. One of the lines in the conversation fires a script and we're going to use that script to track that she's done step number one. Your conversation script might look like this:
void main( )
{
object oPlayer = GetPCSpeaker( );
SetLocalInt(oPlayer,"CurrentStep",1);
}
That's all there is to it. Now a variable named "CurrentStep" has been set equal to 1 and has been attached to oPlayer.
Now, later on, she's got to talk to a second NPC. If she has not talked to the first NPC, the second one will just say "Hello" and basically ignore her. However, if she has talked to the first one then the second will give her more information. The thought-sequence for you, the programmer, is:
- Check the status of CurrentStep
- If CurrentStep = 0, just say "hello"
- If CurrentStep = 1, give new data and set CurrentStep = 2
We'll use GetLocalInt( ) to recover the value of "CurrentStep". Therefore, the code snippet (pseudo-code warning!) is:
object oPlayer = <GetPlayerIdentity>;
int nCurrentStep = GetLocalInt(oPlayer,"CurrentStep");
if(nCurrentStep != 1)
{
//NPC just says Hello
}
else
{
//it is 1, so we'll do our other stuff
//NPC gives more data
SetLocalInt(oPlayer,"CurrentStep",2);
}
So you can see that your various scripts can check the value of "CurrentStep," do whatever you need to do, then set the variable to a new number.
Persistent Variables
When NWN was first released, we world builders were upset to learn that there was no way to have a persistant world. Local variables were lost when the player left the game or the server went down. So each time a player entered the game--as far as NWN was concerned--it was the first time!
We came up with a few ways to work around the issue, but none were very pretty. Let me share them with you!
1: First we have the issue of players losing the local data when they leave the game. This can be worked around by attaching the variables to a placeable rather than the player character. So we'd create an invisible object placeable somewhere and call it...MasterData...or something similar, and we'd attach the variables to that. So if "Jessica" advanced the "ChickenQuest" to step 3, we'd get her name then use string manipulation to add her name to the quest name and that would become our variable. Then we'd attach that variable to MasterData and set the value. Something like:
string sPlayerName = GetName(oPlayer); //assuming you can get it somewhere
string sVariable = sPlayerName + "_ChickenQuest";
object oMasterData = GetObjectByTag("MasterData");
SetLocalString(oMasterData,sVariable,3);
And we've done it. Now the variable isn't stored on the avatar, it's stored on MasterData.
So when it came time to check the status of the quest, of course, we didn't check her. Instead we built the reference to the variable as above and we'd use GetLocalInt( ) off of MasterData.
This worked pretty good except---as you well know--computers crash including the server! And if that happened, or if the server was brought down to do an update, then all those variables on MasterData were lost too.
2: So then we dreamed up making small inventory items and giving them to the player as "markers" of their progress. So I'd make an item with the tag "ChickenQuest_1" and another with "ChickenQuest_2"...and so on. Then I could
- Check the player's inventory and find out which item number he had.
- Take that item away.
- Give him the next in line.
The nice part was that this gave us a form of persistance because inventory items were kept between sessions (as long as we made it so the player couldn't sell / drop / destroy the marker). The bad parts were:
- If I had lots of quests and each quest had lots of steps, my Custom Items palette grew to unwieldy proportions.
- Even if I used "inventory small" items (only 1 square), each marker still used up a square. And if I had lots of quests that the player could work on simultaneously, I could rapidly eat up a lot of inventory space with 1-square markers.
3: Bioware eventually gave in and added primitive database support and commands that would allow the server to write and read data to/from a database. These variables were called "Campaign" variables and worked more-or-less like SetLocal<var> and GetLocal<var>. The thing was slow and cumbersome, but at long last we had persistence.
4: Some intrepid "hackers" created an add-on to NWN that gave us some new commands and the ability to use a slicker, faster, MySQL-type database. You can find that at http://www.nwnx.org/
|