01-08-2009 07:38
A set of interrelated functions to assist in Prim Inventory Management.

Commands

Directory Command

list Dir( string mask, integer type )

returns a list of inventory items of 'type' with names that match 'mask'.

'type' is any valid Inventory constant:

INVENTORY_ALL
INVENTORY_TEXTURE
INVENTORY_SOUND
INVENTORY_LANDMARK
INVENTORY_CLOTHING
INVENTORY_OBJECT
INVENTORY_NOTECARD
INVENTORY_SCRIPT
INVENTORY_BODYPART
INVENTORY_ANIMATION
INVENTORY_GESTURE

'mask' is a case insensitive string with or without prefix and/or post-fix 'wild card' characters.

i.e. "%hok%" will match all names containing hok, Hok, hOk ....... etc.
"lbd%" will match any name beginning with lbd such as 'LBD Short Skirt'
"%temp" will match any name ending in temp such as 'config.temp'
"" will match any string
"string lateral" will match only the exact string but ignores case.

Examples:

List MyInventory = Dir("", INVENTORY_ALL);
List MyDances = Dir("%dance", INVENTORY_ANIMATION);
List LittleBlackDress = Dir("%LBD%", INVENTORY_CLOTHING);

Delete Command

integer Del( string mask, integer type )

Deletes all Items of 'type' that have names matching 'mask' that are not currently locked* and Returns the number of items deleted.
*see Utility Function - Lock for explanation
'mask' and 'item' are as for the Dir() command with the exception that INVENTORY_ALL is not a valid type.

Example:

llSay(0, (string)Del("config %", INVENTORY_NOTECARD) + " duplicate Config files deleted";);

Give Commands

Give( key target, string mask, integer type )

Gives items that match 'mask' and 'type' to the target object or avatar.

'mask' and 'type' are the same as the directory command, target is any valid key.

Example:

Give(llDetectedKey(0), "NewGuest%", INVENTORY_ALL);

will give any items prefixed as "NewGuest" to the Avatar detected, such as a welcome note, rules and landmark.

GiveFolder(key target, string folder, string mask, integer type);

Same as the Give() command but if the 'target' is an avatar, all items are transferred as a single folder.

Example:

GiveFolder(llDetectedKey(0), "HoK Little Black Dress", "%lbd%, INVENTORY_CLOTHING);

Event Handlers

changed event - list Changed()

returns a list in the format [integer change, string name1, string name2]
where change = 1, added
= 0, renamed
=-1, deleted

and name1 and name2 are the effected item names.

Example:

if (change & CHANGED_INVENTORY)
{
list tmp = Changed();
if(llList2Integer(tmp, 0) > 0) llSay(0, llList2String(tmp, 1) + " Added";);
else if(llList2Integer(tmp, 0) < 0) llSay(0, llList2String(tmp, 1) + " Removed";);
else llSay(0, llList2String(tmp, 1) + " Renamed to " + llList2String(tmp, 2));
Directory = Dir("", INVENTORY_ALL);
llSay(0, "Current Invetory = " + llList2CSV(Directory));
}

N.B. it is recommended that every change event includes the line Directory = Dir("", INVENTORY_ALL); so that the script has an up to date inventory list. For the same reason the script initialisation should also read the directory i.e.

state_entry()
{
lock([1, llGetScriptName()]);
Directory = Dir("", INVENTORY_ALL);
llSay(0, "Strating Invetory = " + llList2CSV(Directory));
}

Utility Functions

integer like( string mask, string value )

returns TRUE or FALSE depending on the match of 'value' and 'mask' see http://wiki.secondlife.com/wiki/Like for details

lock(list change)

Implements a simple locking mechanism where the input format is the same as the output of changed()

[change, name1, name2]
where change = 1, add lock to item
= 0, rename locked item
=-1, remove lock from item

Item names in the locked list will not be Given or Deleted even if they match the 'mask'.

examples:

state_entry()
{
lock([1, llGetScriptName()]);
Del("", INVENTORY_ALL);
Directory = Dir("", INVENTORY_ALL);
llSay(0, "Strating Invetory = " + llList2CSV(Directory));
}

will clear the containing prim of all inventory apart from the script.

if you subsequently want to give a copy of the script:

lock([-1, llGetScriptName()]);
Give(Target, INVENTORY_SCRIPT);
lock([1, llGetScriptName()]);

will unlock the script, pass a copy and then relock it.

To keep the locked list in sync with the directory:

if (change & CHANGED_INVENTORY)
{
list tmp = Changed();
lock(tmp);
...
...
...
}

The above will ensure that renamed locked items remain locked and that deleted items are removed from the list.

This script has many applications including simple vendors, notecard givers, greeters etc.
I hope it helps others, please distribute freely.

CODE


//Very Keynes - January 2009
// Directory / Inventory Functions

list Directory;
list locked;
string item;

// Compare Directory to Inventory and return Change and effected name
//
// output [change, name1, name2]
// where change = 1, added
// = 0, renamed
// =-1, deleted
//
list Changed()
{
list tmp;
item = "";
integer y = llGetInventoryNumber(INVENTORY_ALL);
integer x = y - llGetListLength(Directory);
// determain what was added
if( x > 0 )
{
for( x = 0 ; x < y ; x++)
{
item = llGetInventoryName(INVENTORY_ALL, x);
if(llListFindList(Directory, [item]) == -1) x = y;
}
tmp = [1, item];
}

// determain what was removed
else if( x < 0 )
{
x = llGetListLength(Directory);
do
{
item = llList2String(Directory, --x);
if(llGetInventoryType(item) == -1) x = -1;
}
while ( x >= 0 );
tmp = [-1, item];
}

// determain what was changed
else
{
tmp = Dir("", INVENTORY_ALL);
string a;
for(x = llGetListLength(Directory) - 1 ; x >= 0 ; x--)
{
a = llList2String(Directory, x);
if(llListFindList(tmp, [a]) == -1)x = -1;
}
for(x = llGetListLength(tmp) - 1 ; x >= 0 ; x--)
{
item = llList2String(tmp, x);
if(llListFindList(Directory, [item]) == -1)x = -1;
}
tmp = [0, a, item];
}
Directory = Dir("", INVENTORY_ALL);
return tmp;
}

// Delete items from Inventory that match mask and type
// if mask == "" delete all items of type
// INVENTORY_ALL is not valid for type
//
// if a matched item is in the locked list it will not be deleted
//
integer Del(string mask, integer type)
{
integer x;
integer y;
for(x = llGetInventoryNumber(INVENTORY_ALL) - 1 ; x >= 0 ; x-- )
{
item = llGetInventoryName(type, x);
if(like( mask, item ) && llGetInventoryType(item) == type)
{
if(-1 == llListFindList(locked, [item]))
{
llRemoveInventory(item);
y++;
}
}
}
return y;
}

// Return a list of Items that match mask and type
// Dir( "", INVENTORY_ALL ) will return the full inventory
//
list Dir(string mask, integer type)
{
list tmp = [];
integer x;
for(x = llGetInventoryNumber(type) - 1 ; x >= 0 ; x-- )
{
item = llGetInventoryName(type, x);
if(like( mask, item )) tmp += item;
}
return tmp;
}

// Give items that match mask and type
// INVENTORY_ALL is valid
// GiveItem("", INVENTORY_ALL) will give the entier contents
// GiveItem("dance%",INVENTORY_ANIMATION) will give any animations prefixed with dance.
//
Give(key target, string mask, integer type)
{
integer x;
for(x = llGetInventoryNumber(INVENTORY_ALL) - 1 ; x >= 0 ; x-- )
{
item = llGetInventoryName(type, x);
if(like( mask, item ))
{
if(llListFindList(locked, [item]) == -1)
if(INVENTORY_ALL == type || llGetInventoryType(item) == type) llGiveInventory(target, item);
}
}
}

// Give items that match mask and type as a folder
// N.B. Folders only work for Avatars not objects
//
GiveFolder(key target, string folder, string mask, integer type)
{
integer x;
list tmp;
for(x = llGetInventoryNumber(INVENTORY_ALL) - 1 ; x >= 0 ; x-- )
{
item = llGetInventoryName(type, x);
if(like( mask, item ))
{
if(llListFindList(locked, [item]) == -1)
if(INVENTORY_ALL == type || llGetInventoryType(item) == type) tmp += item;
}
}
if(llGetListLength(tmp))llGiveInventoryList(target, folder, tmp);
}
//
// Utility Functions
//
// like("sus%", "Susie"); //will return true, for any value starting with "sus"
// like("%sus%","Susie", ); //will return true, for any value containing "sus"
// like("%sus", "Susie", ); //will return false. This example is looking for a string ending in "sus".
// like("sus", "Susie", ); //will return false. This example is looking for a string matching only "Sus"
//
// N.B. the mask is not case sensative so like("susIe", "Susie", ) will return TRUE.
//
integer like( string mask, string value )
{
if(mask == "")return TRUE;
mask = llToLower(mask);
value = llToLower(value);
integer x = (llGetSubString(mask, 0, 0) == "%") |
((llGetSubString(mask, -1, -1) == "%") << 1);
if(x)
mask = llDeleteSubString(mask, (x / -2), -(x == 2));

integer y = llSubStringIndex(value, mask);
if(~y) {
integer z = llStringLength(value) - llStringLength(mask);
return ((!x && !z)
|| ((x == 1) && (y == z))
|| ((x == 2) && !y)
|| (x == 3));
}
return FALSE;
}
//
// Implements a simple locking mechanism
//
// input format is the same as the output of changed()
// [change, name1, name2]
// where change = 1, add lock to item
// = 0, rename locked item
// =-1, remove lock from item
//
lock(list change)
{
if(llList2Integer(change, 0) > 0) locked += llList2String(change, 1);
else if(llList2Integer(change, 0) < 0)
{
integer x = llListFindList(locked, [llList2String(change, 1)]);
llListReplaceList(locked, [llList2String(change, 2)], x, x);
}
else
{
integer x = llListFindList(locked, [llList2String(change, 1)]);
if(-1 != x)llDeleteSubList(locked, x, x);
}
}
//
// exaple code follows, drop this script in a prim and try playing with the inventory whilst watching chat.
//
default
{
state_entry()
{
lock([1, llGetScriptName()]);
Directory = Dir("", INVENTORY_ALL);
llSay(0, "Strating Invetory = " + llList2CSV(Directory));
}

changed(integer change)
{
if (change & CHANGED_INVENTORY)
{
list tmp = Changed();
if(llList2Integer(tmp, 0) > 0) llSay(0, llList2String(tmp, 1) + " Added");
else if(llList2Integer(tmp, 0) < 0) llSay(0, llList2String(tmp, 1) + " Removed");
else llSay(0, llList2String(tmp, 1) + " Renamed to " + llList2String(tmp, 2));
Directory = Dir("", INVENTORY_ALL);
llSay(0, "Current Invetory = " + llList2CSV(Directory));
}
}
}