Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Clear Inventory function reporting missing item.

Gattz Gilman
Banned from RealLife :/
Join date: 29 Feb 2004
Posts: 316
09-22-2006 19:19
im stumped on this. it will clear the contents except for the script, but will also report this error message Missing inventory item ''
here is the function i wrote:
CODE

ClearInv()
{
integer i;
integer max_inv = llGetInventoryNumber(INVENTORY_ALL);//Get the number of items in contents
llWhisper(0,"Clearing Object Contents.");
if(max_inv > 1)//If contents has more the 1 (meaning it has just this script inside), clear the contents except for this script
{
for(i= 0;i < max_inv;i++)
{
if(llGetInventoryName(INVENTORY_ALL,i) != llGetScriptName())//If returned name is not the name of the script
{
llRemoveInventory(llGetInventoryName(INVENTORY_ALL,i));//remove the item with that name
}
}
}
llWhisper(0,"Inventory Cleared.");
}


any help would be greatly appreciated.
as far as i can tell, this might be a bug.
_____________________
Aakanaar LaSalle
Registered User
Join date: 1 Sep 2006
Posts: 132
09-22-2006 19:44
I was about to suggest that perhaps it had something to do with the inventory being zero based, but getinventorynumber isn't.. but then I noticed you're checking if i < max_inv.. so nvm.. you got that figgured already...

perhaps there is some inventory item that doesn't have a name and remains in inventory? Something that returns the empty string? I'll have to check. For now, the obvious fix is to check if(llGetInventoryName(INVENTORY_ALL,i) != llGetScriptName() && llGetInventoryName(INVENTORY_ALL,i) != "";)

But that's just a quick fix

Edit: From what I can see, I would figgure that the llGetInventoryNumber(INVENTORY_ALL); is returning one too many. But to test this, make a quick test script and put a number of object in it, then have it return the number of items correctly. If it doesn't, report it. If it does, then the problem must be in the llGetInventoryName function.. or perhaps how it compares INVENTORY_ALL as opposed to using various INVENTORY_* constants.

I dunno.. it does seem to be a bug.
Don Misfit
Registered User
Join date: 1 Jun 2006
Posts: 60
09-22-2006 23:15
The for() loop logic is faulty.

Say you start with 6 items in inv (we don't care about one being the script right now). What you are doing is:

CODE

Remove item (name of item 0)
// 5 items left
Remove item (name of item 1)
// 4 items left
Remove item (name of item 2)
// 3 items left
Remove item (name of item 3)
// 2 items left
Remove item (name of item 4) //---- ooops, can't get the name of the 4th of 2 items


A better way to do it would be:

CODE

ClearInv() {
list lst = [] ;
integer i;
//Get the number of items in contents
integer max_inv = llGetInventoryNumber(INVENTORY_ALL);
llWhisper(0,"Clearing Object Contents.");
//If contents has more the 1 (meaning it has just this script inside),
//clear the contents except for this script
if(max_inv > 1) {
// build a list of inv item names, excluding this script
for(i= 0;i < max_inv;i++) {
//If returned name is not the name of the script
if(llGetInventoryName(INVENTORY_ALL,i) != llGetScriptName()) {
lst += [llGetInventoryName(INVENTORY_ALL,i)] ;
}
}
// step through that list, removing each item
for(i= 0;i < max_inv - 1;i++) {
// remove each item by name
llRemoveInventory(llList2String(lst, i));
}
}
llWhisper(0,"Inventory Cleared.");
}
Lazink Maeterlinck
Registered User
Join date: 8 Nov 2005
Posts: 332
09-23-2006 01:23
ok, couldn't help myself. Was bored and reading through the forums and saw this thread, so desided to tackle the problem. Here is the code, enjoy.. tested and works, only problem is it MAY run into memory overflow because of the limited 16k memory, I don't know. So if you have that problem let me know :)

CODE


//Short Script that will erase everything in the contents of an object
//but the script itself.

//Storing the script name (see state_entry)
string SCRIPT_NAME;

//Name: ClearInventory
//Desc: A recursive algorithm that removes every item in the objects
// inventory except the script in SCRIPT_NAME
//Input: None
//Output: All items in the object are deleted save this script.
//Globals Used: string SCRIPT_NAME
//Usage: ClearInventory()
ClearInventory()
{
//Check to see if just the script is left
if(llGetInventoryNumber(INVENTORY_ALL) == 1)
{
return;
}
//More then the script left, so erase the next item.
else
{
string delete_item = llGetInventoryName(INVENTORY_ALL, 0);
if(delete_item == SCRIPT_NAME)
{
llRemoveInventory(llGetInventoryName(INVENTORY_ALL, 1));
}
else
{
llRemoveInventory(llGetInventoryName(INVENTORY_ALL, 0));
}
ClearInventory();
}
}

default
{
state_entry()
{
//Get the script name
SCRIPT_NAME = llGetScriptName();
}

touch_start(integer num_detected)
{
//on touch, clear the inventory.
ClearInventory();
}
}


you could even get rid of SCRIPT_NAME and use llGetScriptName() in the ClearInventory function itself. Thats a personal choice :)
Gattz Gilman
Banned from RealLife :/
Join date: 29 Feb 2004
Posts: 316
09-23-2006 05:02
From: Don Misfit
The for() loop logic is faulty.

Say you start with 6 items in inv (we don't care about one being the script right now). What you are doing is:

CODE

Remove item (name of item 0)
// 5 items left
Remove item (name of item 1)
// 4 items left
Remove item (name of item 2)
// 3 items left
Remove item (name of item 3)
// 2 items left
Remove item (name of item 4) //---- ooops, can't get the name of the 4th of 2 items


A better way to do it would be:

CODE

ClearInv() {
list lst = [] ;
integer i;
//Get the number of items in contents
integer max_inv = llGetInventoryNumber(INVENTORY_ALL);
llWhisper(0,"Clearing Object Contents.");
//If contents has more the 1 (meaning it has just this script inside),
//clear the contents except for this script
if(max_inv > 1) {
// build a list of inv item names, excluding this script
for(i= 0;i < max_inv;i++) {
//If returned name is not the name of the script
if(llGetInventoryName(INVENTORY_ALL,i) != llGetScriptName()) {
lst += [llGetInventoryName(INVENTORY_ALL,i)] ;
}
}
// step through that list, removing each item
for(i= 0;i < max_inv - 1;i++) {
// remove each item by name
llRemoveInventory(llList2String(lst, i));
}
}
llWhisper(0,"Inventory Cleared.");
}


ya, i figured that out while i was at work lol.

But a big thank you to all the people who posted to help out.
+1 to the forums staying open!
_____________________
Lex Neva
wears dorky glasses
Join date: 27 Nov 2004
Posts: 1,361
09-23-2006 11:06
Another way to deal with this is to start your loop counter at (max_inv - 1), and count down to i >= 0. That way, when you delete stuff, it's only going to reorder the stuff at the end of the list.
Aakanaar LaSalle
Registered User
Join date: 1 Sep 2006
Posts: 132
09-23-2006 11:36
Yea, I was gonna say the same thing.. When i read the post on the logic, it made sense to me.. the counter in the for loop does not have to go up.. You can have it go down, or multiply it by the third sunday of april if you wanted.

So I'd set that as

for (i = max_inv - 1; i >=0, i--)
Mevo Syaka
Ganja Guru
Join date: 12 Dec 2006
Posts: 33
04-30-2007 04:06
From: Lazink Maeterlinck
ok, couldn't help myself. Was bored and reading through the forums and saw this thread, so desided to tackle the problem. Here is the code, enjoy.. tested and works, only problem is it MAY run into memory overflow because of the limited 16k memory, I don't know. So if you have that problem let me know :)

CODE


//Short Script that will erase everything in the contents of an object
//but the script itself.

//Storing the script name (see state_entry)
string SCRIPT_NAME;

//Name: ClearInventory
//Desc: A recursive algorithm that removes every item in the objects
// inventory except the script in SCRIPT_NAME
//Input: None
//Output: All items in the object are deleted save this script.
//Globals Used: string SCRIPT_NAME
//Usage: ClearInventory()
ClearInventory()
{
//Check to see if just the script is left
if(llGetInventoryNumber(INVENTORY_ALL) == 1)
{
return;
}
//More then the script left, so erase the next item.
else
{
string delete_item = llGetInventoryName(INVENTORY_ALL, 0);
if(delete_item == SCRIPT_NAME)
{
llRemoveInventory(llGetInventoryName(INVENTORY_ALL, 1));
}
else
{
llRemoveInventory(llGetInventoryName(INVENTORY_ALL, 0));
}
ClearInventory();
}
}

default
{
state_entry()
{
//Get the script name
SCRIPT_NAME = llGetScriptName();
}

touch_start(integer num_detected)
{
//on touch, clear the inventory.
ClearInventory();
}
}


you could even get rid of SCRIPT_NAME and use llGetScriptName() in the ClearInventory function itself. Thats a personal choice :)


How would I edit this to allow it to delete everything except the script itself and a notecard?
_____________________
FourTwenty Headshop



Yosef Enoch
Registered User
Join date: 10 May 2007
Posts: 1
I got bored and hacked this too!
02-11-2009 08:11
Hey guys, I ran across this thread by accident and decided to rework the script to allow for toggling of script delete and also allowing for input of a specific list of items NOT to delete. It's heavily commented for those who'd like to know how I made this. Hope you enjoy~

//Name: Clear Inventory w/ Item Ignore
//Desc: short script that will erase everything in the contents of an object besides
//for a list of objects and the script itself if "delete_this_script = FALSE"
//Author: Yosef Enoch
//Date: Feb 11, 2009

//USER SETTINGS - Edit these settings to suit your needs.
integer delete_this_script = FALSE; //set this to TRUE or FALSE
list dont_delete_these = ["item1", "item2", "item3"]; //list of items NOT to delete

//DO NOT EDIT BELOW HERE UNLESS YOU'RE INTERESTED IN EXPERIMENTING
clearInventory(){
//notify user that action is being taken.
llSay(0, "Clearing Inventory!";);
//create a variable with the total number of inventory items
//this number will be decreased later throughout the loop.
integer total_inv = llGetInventoryNumber(INVENTORY_ALL);
//if the total number is more than 1, that means there is something besides the script
//so we cycle through, otherwise we we skip to the last part and delete this script.
if(total_inv > 1){
//add script name to list of things not to delete, since even if we do delete this,
//it won't be till the very end.
//Using ";(some_list = []) + some_list +" instead of "some_list +"
//reduces memory fragmentation.
dont_delete_these = (dont_delete_these = []) + dont_delete_these + llGetScriptName();
//loop through all inventory names, starting with the highest number (last item)
//and ending with the first item. loops as long as we are not at 0 items.
while(total_inv > 0){
//adding the current item to a string. we use (total_inv - 1) since the
//inventory index starts at 0 so 0 is the first item, 1 is the second item and so on
string current_item = llGetInventoryName(INVENTORY_ALL, total_inv - 1); //
//search the don_delete_these list for the current item and remove the current item
//only if llListFindList returns -1 which means it can't be found on the list.
if(llListFindList(dont_delete_these, [current_item]) == -1){
llRemoveInventory(current_item);
}
//decrease the inventory number to go to the next item.
//very important to prevent the loop from going forever.
--total_inv;
}
}
//if we set delete_this_script to true, delete the script
//if(delete_this_script == TRUE) is the same as if(delete_this_script)
if(delete_this_script){
llRemoveInventory(llGetScriptName());
}
}

default
{
state_entry(){
llSay(0, "Touch me to clear inventory";);
}

touch_start(integer num_detected){
//on touch start, clear the inventory.
clearInventory();
}

}