Robert Straughan - Capture Glory Strip Tease

From NWN Lexicon
Jump to: navigation, search

Robert Straughan - Capture Glory: Strip Tease

Starting Out


I've decided I'd like the player to start out stripped of all their equipment. I'd also like to make the player start at level 3, instead of level 1. Finally, it would be nice to have them start out lying down on the ground.


This can be done by writing the OnClientEnter script like the following:


//Script "tutorial_oce"
void main()
{
     object oEnter = GetEnteringObject();
     object oItem = GetFirstItemInInventory(oEnter);
 
     while (GetIsObjectValid(oItem))
     {
          DestroyObject(oItem);
          oItem = GetNextItemInInventory(oEnter);
     }
     int nSlot;
     for (nSlot = 0; nSlot < NUM_INVENTORY_SLOTS; nSlot++)
     {
          oItem = GetItemInSlot(nSlot, oEnter);
          DestroyObject(oItem);
     }
 
     TakeGoldFromCreature(GetGold(oEnter), oEnter, TRUE);
     object oClothes = CreateItemOnObject("NW_CLOTH024", oEnter);
     if (GetXP(oEnter) < 3000)
          SetXP(oEnter, 3000);
     ApplyEffectToObject(DURATION_TYPE_INSTANT, 
          EffectDeath(), 
          oEnter
     );
}

This script introduces two new conditional statements, while and for. They are both exactly the same as if statements, but they work in different ways.


A while loop is exactly like an if statement, except that it will continue to run the script within its curly brackets for as long as the conditional is true. Think of it as an if statement that jumps back to the if part every time it gets to the end.


The for statement is also a while loop, but it has some in built features. The actual conditional statement is the one in the middle of the brackets, nSlot <= 18. For as long as that is true, the lines in the curly brackets will continue to run.


The first statement in a for loop is an initialise statement. You can only initialise a variable here, you cannot declare it also (hence why nSlot is declared prior to the for statement). Since I want nSlot to be zero, and I can't skip the initialise section, I have to put nSlot = 0 even though it was zero to start with.


The last section is the mathematical statement. Everytime the for loops back to the beginning, while it only does the initialise section once, if the conditional remains true, then it will do the maths found at the end section of the for statement.


In this case, I've chosen to increment nSlot. The proper way of writing this down is nSlot = nSlot + 1. That would mean that nSlot becomes whatever it previously was, plus one.


However, to tidy things up, the language allows you to simply add ++ after the variable name. This means it will increase the variable by one. For future note, -- does mean decrement.


So, now that you know how those two work, can you see what this script does? Take a look, then read on.


The first thing it does is declare and initialise the object entering, since this is an OnEnter handler, and gets the first item in that object's inventory.


I want to strip the target of their inventory, so I'm going to need to keep on going through every object they have until there are no more. For this, I use a while loop, which runs for as long as the item in the inventory is a valid target.


I have used DestroyObject to get rid of the item in the inventory. But I can't just leave it like that, since oItem will no longer be valid (be careful with while loops, if you don't change what you're checking against, it may get stuck).


So, I then use the GetNextItemInInventory function to cycle through the objects in the target's inventory. For as long as they have objects, they will continue being destroyed.


But this only deals with objects in the inventory, what about ones they have equipped. For this, we use the for loop. It will run through the loop 19 times, since we start at 0, and add 1 every time the loop finishes, until the conditional is no longer true.


But what about the nSlot value. If you look at the definition of the GetItemInSlot function, you'll notice it asks for an integer equivalent named INVENTORY_SLOT_.


As you learned earlier with DURATION_TYPE, the text is really just representing an integer value. Looking in nwscript.nss reveals that the INVENTORY_SLOT values run from 0-18.


So we can use actual integer values, and not the text form. There's no difference, except with the true integer form, we can actually do mathematical operations with it. In this case, incrementing it.


So, that sorts out the strip items. What about the next two lines? Well, it looks like we're taking gold from the entering object. By using the GetGold function, I don't have to state how much gold to take, since the script will find out how much the object has.


The CreateItemOnObject function is actually to give the entering object back some clothes. Since I've just stripped every object off the entering object, it would be nice to have something to wear.


Notice that I've actually declared it properly. This is for no technical reason, but it shows that you can use the Create functions either to declare new objects in a script, or to simply create them in the module.


The next two lines should be easy. A simple if statement, which checks to see if the XP of the entering object is less than 3000, and if so, sets their XP to that amount.


Paying attention to what variable type a function returns is crucial with numbers, as some return floats, and some return integers. The two variable types are not interchangeable.


Recognise the last one? Whoops! The entering object dies! This is because there currently isn't a 'lying down' or 'unconscious' effect in the engine. You could use EffectKnockdown, but they remain awake for it.


This throws up an immediate problem. Our OnDeath script is going to fire when the player enters! This is not very good. So we need to go back and alter it to ensure it doesn't happen.


Can you guess what I'm going to do with it? Take a look:


//Script "tutorial_odth"
void main()
{
     object oPlayer = GetLastPlayerDied();
     if (GetLocalInt(oPlayer, "DIED_ONCE") == 0)
     {
          SetLocalInt(oPlayer, "DIED_ONCE", 1);
          return;
     }
     PopUpDeathGUIPanel(oPlayer, TRUE, FALSE, 1, 
          "If you press respawn, you will be resurrected at " +
          "the beginning of the level, and will suffer a -1 to " +
          "your constitution."
     );
}

Notice the use of local variables. I'm getting a local variable on the last player who died. If it is equal to zero, which it will be by default since I've not touched it yet, then it sets it to 1, meaning from now on, the if statement won't do what's in its curly brackets.


So does that stop the death panel? No, what it does is prevent the line return; from being run. The return is a built in function to the code. It immediately quits the script (this is not entirely accurate, but you'll learn that a bit later).


So the first time the player dies, since the if statement will be true, the script will quit before it can put the death panel up. But after that, it will always display the panel. Simple, huh?



What have you learned?


Well, I'm hoping you'll be beginning to see just how simple scripting really is. Once you've learned the basic of how the script all plugs into each other, it just becomes a case of asking yourself "What do I want this script to do?"


If you need to, try writing the script on this page out for yourself in the editor, or re-read what you've just read. Otherwise, move along.



Tasks


Notice how I strip all equipment off the entering object, and then place some clothes back onto the object. What I want you to try and do is to change the script so that instead of placing the new clothes onto the object, prevent the loops which strip the object of its items from taking the objects clothes that it currently has on.



Hint


You need to place an if statement into the for loop, that checks to see if the current value of nSlot is not equal to the same one as the chest slot value. This way, if it finds that the current slot is the chest slot, it won't do the DestroyObject function.

Curly Brackets


As you've learned by now, all script is contained within curly brackets, to let the compiler know how everything is laid out.


You've seen me use if statements without them though, but only because only one line of code runs if the if statement is true.


This is not the same for the other conditional statements. It only works with if statements.


First/Next


There are a number of functions in NWScript which have GetFirst/GetNext in their names.


These are designed to work with loops, in order to cycle through a particular type of object in the module.


Conversion


You may notice in the list of functions a number of functions which refer to the conversion of one variable type to another.


Namely, these allow the transferring of integers, floats and strings to and from one type or the other.


This can be useful when using variables of different sorts and needing to perform calculations with them, since you can't do mathematical work with different variable types.


Also note that since integers are whole numbers, doing division with them can produce unwanted results.


If doing division with integers, use the IntToFloat function to convert the numbers to ones which can handle decimal points.


Also note that in a number of scripts, you may or may not see an f after a floating-point number. The f is used to force the number to remain a float, and not do any rounding of numbers.





 author: Robert Straughan, editors: Charles Feduke, Mistress, contributor: Ken Cotterill