User:SuzannaLinn Resident/ScriptingClasses/Give Inventory

From Second Life Wiki
< User:SuzannaLinn Resident‎ | ScriptingClasses
Revision as of 02:48, 13 November 2024 by SuzannaLinn Resident (talk | contribs) (Created page with "= Give Inventory = == Monday class == We will chat a lot about "inventory" today. This inventory is always refered to the contents of an object. It has nothing to do with t...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Give Inventory

Monday class

We will chat a lot about "inventory" today. This inventory is always refered to the contents of an object. It has nothing to do with the big inventory where we have our clothes, landmards and everything else.


List the contents

-> Object 1 Inventory list contents

Before giving anything, let's start listing the contents of the box.

We will use a loop to look at all the items one by one.

We will use the keyword "for". Let's see how it works.

The "for" loop is used to repeat a block of code a specific number of times. It's helpful when we need to perform the same action multiple times, such as iterating through a list of items or executing a series of steps.

Here's a basic structure of a "for" loop:

	for (   initialization;    condition;    increment/decrement   )
	{
    		// Code to be executed repeatedly
	}

And, for instance, to say the numbers from 1 to 10:

	integer number;
	for ( number = 1; number < 11; number++ )
	{
    		llOwnerSay( (string)number );
	}

In the inizialitation part we initialize a variable that will be used to control the loop:

  • number = 0;

In the condition part there is a condition that must be true for the loop to continue iterating. The loop will keep executing as long as this condition remains true. Once the condition becomes false, the loop will terminate:

  • number < 10;

In the increment/decrement part we modify the variable used for controlling the loop. Typically, we'll either increment or decrement the variable. This step occurs at the end of each iteration:

  • number++

In our example, number is incremented by 1 after each iteration.

After saying the number 10, number is incremented to 11, the condition number<11 becomes false, the code is not executed any more and the loop finishes.

The three parts (init, condition, inc/dec) of the "for" have to be enclosed in parentheses: "(" and ")". The code executed in the loop have to be enclosed in curly brackets: "{" and "}"

First we need to know how many items there are inside the object.

The function is llGetInventoryNumber(). It has the parameter "type". The type can be notecard, landmark, texture, object, etc. It can also be all the items, with the constant INVENTORY_ALL.

We get the quantity of items with this:

  • totalItems = llGetInventoryNumber( INVENTORY_ALL );

The items are numbered starting with 0, so we want to loop from 0 to totalItems-1 one by one, with this:

  • for ( itemNumber = 0; itemNumber < totalItems; itemNumber++ )

Inside the "for" loop we want to get the name and the type of each item.

llGetInventoryName() gives us the name. It has 2 parameters, "type" and "number". Type is the same than in llGetInventoryCounter(), and we must use the same to make it work. So it will be:

  • itemName = llGetInventoryName( INVENTORY_ALL, itemNumber );

The type comes from llGetInventoryType() and the parameters are different. This function has one parameter that is "name". We need to get the name of the item before being able to get its type. And it is:

  • itemType = llGetInventoryType( itemName );

The type is a numeric code, from 0 to 21, and we want to show the description of the type.

To find our type description we will use the list TYPES.

"list" is a type of variable like "string", "integer" or "float".

But instead of one value, it can take, as its name say, a list of values, for instance:

  • list fruits = [ "Apple", "Banana", "Pear", "Melon", "Apricot" ];
  • list primeNumbers = [ 2, 3, 5, 7, 11, 13, 17, 19 ];

The values have to be enclosed in square brackets and can be of any type, also floats, vectors, keys, etc. A list can have a mix of values of different types, but we are not going to mix types.

Our list TYPES has 22 values, one for each inventory type. You see that some values are empty, because not all the type codes exist.

The function to get a value from a list is llList2String and its parameters are the list and the position that we want.

  • itemTypeDescription=llList2String( TYPES, itemType );

And finally we will say to ourselves, with llOwnerSay, the name and type of the object:

  • llOwnerSay( itemName + " (" + itemTypeDescription + ")" );

At the end of the script there is the list of the constants for the inventory types.

Now touch your box, to see your list of contents.


Give the contents

-> Object 2 Inventory give contents

Now it's time to make the box give the contents.

We will write a new function: giveInventory( key Receiver ). The receiver is the person who has touched the box, so you can give the contents to anybody. Later we will see how to call the function from the event touch_start.

We start exactly as in the list function, with the "for" loop:

	TottalItems = llGetInventoryNumber( INVENTORY_ALL );
	for ( itemNumber = 0; itemNumber < totalItems; itemNumber++ )

and getting name and type:

	itemName = llGetInventoryName( INVENTORY_ALL, itemNumber );
	itemType = llGetInventoryType( itemName  );

We usually don't want to give the scripts, so in this sample we will exclude them:

	if ( itemType != INVENTORY_SCRIPT )

And the function to give an item from the inventory is llGiveInventory(). It has two parameters: the receiver and the name of the item:

	llGiveInventory( receiver, itemName );

And finally in the event touch_start we will get the toucher with the llDetectedKey(0) function:

	toucher = llDetectedKey( 0 );

and call our function:

	giveInventory( toucher );

We could have done giveInventory( llDetectedKey( 0 ) ) and avoid creating the variable toucher. But I suggest to do it step by step. It makes a more clear script, for us and for other scripters whom we give it.

Now touch your box, to receive a copy of its contents. Also you can touch the box of other students.


Give the contents in a folder

.> Object 3 Inventory give folder

You have seen that this way of giving the contents is a bit uncomfortable. We get a window to close for each item and notecards opened.

We can do it better, giving the contents inside a folder.

The function to give a folder is llGiveInventoryList(). It has 3 parameters: the receiver, the folder name and a list of the items to give.

So before giving the contents we need to make a list with them.

We create am enpty list:

  • list items;

We can add items to the end of a list using the "+" operator: items = items + itemName, or in the "+=" short version:

  • items += itemName;

We write this line inside the "for" loop, and remove the previous llGiveInventory() line

Now we need a name for the folder, we will use the description of our box. We get the description of an object with llGetObjectDesc() :

  • folderName = llGetObjectDesc();

Using the object description is a way that any user of our "giving" box can change the folder name without touching the script.

And, out of the "for" loop we write:

  • llGiveInventoryList( receiver, folderName, items );

Now touch your box, to receive a folder with its contents. Also you can touch the box of other students.


Delete inventory

-> Object 4 Inventory remove content

As our last script we will remove one item from the inventory.

You have seen all the time that there is a notecard called "Unuseful notecard". Let's go to remove it.

The function is llRemoveInventory() and it has one parameter: the name of the item

  • llRemoveInventory( name );

But there is a problem. If the item that we want to remove doesn't exist the function gives an error. And if we call the function twice the first time will remove the item and the second time will not be there and we will get an error.

We will write a function that checks if an item exists:

  • integer itemExists( string name )

I suggest to write a new function each time that we are going to write something that could be reused. You can copy the itemExists function to another script any time that you need to check for something in the contents, in any kind of script. If you have it mixed with the removing function you can't reuse it.

Our iItemExists function returns an integer and we will return one of the constants TRUE or FALSE.

To know if the item is in the contents we will use llGetInventoryType(). We have already seen that it gives us the type of the item, but when the item doesn't exist it returns INVENTORY_NONE.

We will use an "if" to assign a value of TRUE or FALSE that we will return with the "return" command:

	if ( llGetInventoryType( name ) == INVENTORY_NONE ) {
         		exists = FALSE;
 	} else {
       		 exists = TRUE;
	}
	 return exists;

Remember to always use the double "==" when comparing values. The single "=" is only to assign a value to a variable. In this case it would show an error when saving, but in other cases can lead to a very confusing results.

We could also have written the itemExists function in only one line:

  • return ( llGetInventoryType( name ) == INVENTORY_NONE ).

I suggest to use the more detailed, although longer, way for clarity.

Now we will write our removeInventory( string name ) function that we will call from the event touch_start:

	removeInventory( string name ) {
    		if (itemExists( name ) ) {
        			llRemoveInventory( name );
       			llOwnerSay( "The item '" + name + "' has been removed" );
    		} else {
        			llOwnerSay( "The item '" + name + "' does not exist" );
    		}
	}

And call it inside the event touch_start:

	removeInventory( "Unuseful notecard" );

Now touch your box, to delete the notecard. And touch again to get the message that the notecard is not there.


Answers to questions

Are the curly braces always needed in a loop for?

  • Yes, unless it's a single statement. If there is only one statement the braces aren't needed, but is good to add them for clarity.

Does the script gets the information from the items contained in the object?

  • Yes, all these functions starting with llGetInventory*** get the info from the contents of the object.

What are the code numbers of the inventory types?

  • 0 - INVENTORY_TEXTURE
  • 1 - INVENTORY_SOUND
  • 3 - INVENTORY_LANDMARK
  • 5- INVENTORY_CLOTHING
  • 6 - INVENTORY_OBJECT
  • 7 - NVENTORY_NOTECARD
  • 10- INVENTORY_SCRIPT
  • 13- INVENTORY_BODYPART
  • 20- INVENTORY_ANIMATION
  • 21- INVENTORY_GESTURE

Can we assign another number to those items?

  • No, we can't assign another number of code to the types, it's predefined by LSL.
  • The constants are declared by the LL engineers and we can't change the way LSL functions handle them.
  • llGetInventoryType() always returns the codes that LSL has predefined, we need to work with the LSL codes.

What is the type with code 2, between "Sound" and "Landmark"?

  • It doesn't exist, the numbers of types are not correlative, there are codes not used
  • In our list of descriptions of types, we have an empty item for the ones that doesn't exist.

If it doesn't exist, why is it in our list of type descriptions?

  • Because we use the code of the type as the index of the list.
  • For instance, "clothing" has the type code 5 (the constant INVENTORY_CLOTHING has a value of 5) and we need "clothing" to be in the index 5 (position 6th) in the list to obtain the description using the code of type.
  • The list is a list of names indexed by the INVENTORY_xxx numbers. Those numbers are not all used at present. the "" entries are placeholders for unused values so we can index into the list by the appropriate number to get the text.

What happens if we leave the "" out of the list?

  • llGetInventoryType() returns an integer, the code of the type. For instance, "landmark" is 3.
  • If we remove the "" in the list:
    • ["Texture", "Sound", "Landmark", "Clothing", "Object", ...
  • When we go to obtain index 3 instead of "landmark" we will get "clothing".
  • The index would be off by the number of ""s you left out after the point you left them out.

Do we either have to identify the type or the type number, to get the right inventory item?

  • The type is a number, we use the constants INVENTORY_*** instead of the number
  • Internally to LSL, there is no difference between type, and type number, The constants are a convenience for coders not to have remember that 21 means the inventory item is a gesture.

Does the script work for any number of items in the inventory?

  • Yes, we get the quantity of items with llGetInventoryNumber() and loop on all of them.

Could I add a T shirt and a cat and it would just list them?

  • Yes, it will. Play with things. Break stuff! Much of what you will learn about scripting will come from all the ways you will find to do thing wrong, wrong, wrong!

Why does llGetInventory*** functions use names and not uuid's?

  • These functions are working from Item Name instead of by Item Key as many things do, because for some types of items, like textures, the keys associated in inventory (which is on an inventory server, not the sim per se) can be used to bypass the permission system's transfer lock.

Does != mean "is not"?

  • != means "not equal".

Why are we excluding the scripts when we give the items?

  • When we have an object that give contents, usually we have the contents and a script to send them, and we don't want to send the script, so we exclude it.
  • But we can send scripts it, if we want, like any other items.

Does llRemoveInventory() remove the notecard from object's content?

  • Yes, llRemoveInventory() removes one item from the object contents.
  • All of these functions operate on object Inventory, not our inventory.

Can the script remove any notecard?

  • Yes, and any other item that is in the contents of the object where the script is.
  • Including the script itself, which is often useful if the script is a one-shot for setting object properties.
  • Another use for this feature is cleaning up a mail/suggestion box where people can drop notes for you to read, or a game like a treasure hunt where the given item is removed when someone finds it.

Is there no error return from llRemoveInventory()?

  • llRemoveInventory() doesn't have a return value.
  • If we use it with an item that doesn't exist, the script throws an error and stops.

Is it possible to write a script that can remove any file type (for example all notecards or all ladmarks)?

  • Yes, we would loop through all items of the desired type.

Are there other ways to write our function itemExists()?

  • Yes, itemExists is another example of varying coding standards.
    • Instead of setting a variable, we could just return FALSE or TRUE directly in the if statement. This would be slightly more efficient.
    • By setting the variable, and returning at a single point, we are adhering to a SIngle Point of Return standard.
    • There are some fairly arcane reasons why this is a good idea put forth by people who think about how to standardize good coding practices.
    • In essence they boil down to, it makes it easier to add functionality or debugging code to an existing function if you have only a single point at which you know everything the existing function is doing is done.

Could we add an item with the script to the object inventory?

  • No, there is no way to do it. We can give items, but not add them.
  • The item has to be something the object has control of, it can't grab something from someone's inventory or from another object's. However, one object can give something in its inventory to another object, thus adding to the other object's inventory under script control.

Can we give an item from another box if they are linked?

  • No, we would need a script in the other box too.

Can RLV access our inventory?

  • Yes, RLV can access our inventory, but only some especially designed folders.