Giver

From Second Life Wiki

Jump to: navigation, search

Created by Kira Komarov.

Contents

ChangeLog

  • 17-March-2012

After some discussions with LuckyBuck Resident, support for items with names exceeding the dialog button limit was added.

  • 02-December-2011

Added notecard based giver variant, for holidays and such events.

  • 23-November-2011

Added object types and a variation to give out only one single object. Please refer to the configuration section to configure your script before you use it.

Motivation

I have seen a number of commercial givers out there on the marketplace and wanted to implement a free one. What is does is offer a menu on touch with forward and backward browsing capabilities and lists the items contained in the same prim as the script resides in. When a user clicks a button, named after the object in the prim, the script gives a copy of that object to the user.

Applications

This script has multiple applications depending on what you want to use it for; for example, a simple application would be an automated bartender that people can click to browse and get a drink. Another example would be landmark and notecard givers.

Limitations

One limitation is that the objects within the prim should have names smaller in length than 24 bytes. Also, please bear in mind that the menu buttons are generated from the inventory contents of the prim, so if your object has a long name, it will be truncated when it appears on the button. A simple way to avoid this is to give simple and meaningful names to the items in the giver prim. In case you want to add items or remove items, just reset the script after you have done your adjustments: I could have included an event handler to trigger when the inventory changes but I preferred to keep things simple.

Code

//////////////////////////////////////////////////////////
// [K] Kira Komarov - 2011, License: GPLv3              //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////
//                   CONFIGURATION                      //
//////////////////////////////////////////////////////////
//                                                      //
 
// The possible values for this parameter are:
// 0 for textures.
// 1 for sound.
// 3 for landmarks.
// 5 for clothing.
// 6 for objects.
// 7 for notecards.
// 10 for scripts (the giver script will be excluded).
// 13 for body parts.
// 20 for animations.
// 21 for gestures.
 
// The type is 6, so using the reference above, this 
// script will hand out objects.
integer GIVE_TYPE = 3; 
 
//                                                      //
//                  END CONFIGURATION                   //
//////////////////////////////////////////////////////////
 
integer _runner = 0;
integer _comHandle = 0;
list _cList = [];
list _storage = [];
 
list mFwd() {
    if(_runner+1>llGetListLength(_storage)) return _cList;
    return (_cList = llListInsertList(llListInsertList(llList2List(_storage, ++_runner, _runner+=9), ["<= Back"], 0), ["Next =>"], 2));
} 
 
list mBwd() {
    if(_runner-19<0) return _cList;
    return (_cList = llListInsertList(llListInsertList(llList2List(_storage, _runner-=19, _runner+=9), ["<= Back"], 0), ["Next =>"], 2));
}
 
default
{
    state_entry()
    {
        _storage = [];
        integer itra=llGetInventoryNumber(GIVE_TYPE)-1;
        do {
            if(llGetInventoryName(GIVE_TYPE, itra) != llGetScriptName())
                _storage += llGetSubString(llGetInventoryName(GIVE_TYPE, itra), 0, 23);
        } while(--itra>=0);
        _cList = llListInsertList(llListInsertList(llList2List(_storage, _runner, _runner+=9), ["<= Back"], 0), ["Next =>"], 2);
    }
 
    listen( integer channel, string name, key id, string message ) {
        if(message == "<= Back") {
            llDialog(id, "Please browse the available items:\n", mBwd(), channel);
            return;
        }
 
        if(message == "Next =>") {
            llDialog(id, "Please browse the available items:\n", mFwd(), channel);
            return;
        }
        integer itra = llGetInventoryNumber(GIVE_TYPE)-1;
        do {
            if(llGetSubString(message, 0, 23) == llGetSubString(llGetInventoryName(GIVE_TYPE, itra), 0, 23)) {
                llGiveInventory(id, llGetInventoryName(GIVE_TYPE, itra));
                jump close;
            }
        } while(--itra>=0);
@close;
        llListenRemove(_comHandle);
    }
    touch_start(integer total_number)
    {
        integer comChannel = ((integer)("0x"+llGetSubString((string)llGetKey(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        _comHandle = llListen(comChannel, llDetectedName(0), llDetectedKey(0), "");
        llDialog(llDetectedKey(0), "Please browse the available items:\n", _cList, comChannel);
    }
}

Code variation: One object / avatar

//////////////////////////////////////////////////////////
// [K] Kira Komarov - 2011, License: GPLv3              //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////
//                   CONFIGURATION                      //
//////////////////////////////////////////////////////////
//                                                      //
 
// The possible values for this parameter are:
// 0 for textures.
// 1 for sound.
// 3 for landmarks.
// 5 for clothing.
// 6 for objects.
// 7 for notecards.
// 10 for scripts (the giver script will be excluded).
// 13 for body parts.
// 20 for animations.
// 21 for gestures.
 
// The type is 6, so using the reference above, this 
// script will hand out objects.
integer GIVE_TYPE = 3; 
 
//                                                      //
//                  END CONFIGURATION                   //
//////////////////////////////////////////////////////////
 
integer _runner = 0;
integer _comHandle = 0;
list _cList = [];
list _storage = [];
list previous_users = [];
 
list mFwd() {
    if(_runner+1>llGetListLength(_storage)) return _cList;
    return (_cList = llListInsertList(llListInsertList(llList2List(_storage, ++_runner, _runner+=9), ["<= Back"], 0), ["Next =>"], 2));
} 
 
list mBwd() {
    if(_runner-19<0) return _cList;
    return (_cList = llListInsertList(llListInsertList(llList2List(_storage, _runner-=19, _runner+=9), ["<= Back"], 0), ["Next =>"], 2));
}
 
default
{
    state_entry()
    {
        _storage = previous_users = [];
        integer itra=llGetInventoryNumber(GIVE_TYPE)-1;
        do {
            if(llGetInventoryName(GIVE_TYPE, itra) != llGetScriptName())
                _storage += llGetSubString(llGetInventoryName(GIVE_TYPE, itra), 0, 23);
        } while(--itra>=0);
        _cList = llListInsertList(llListInsertList(llList2List(_storage, _runner, _runner+=9), ["<= Back"], 0), ["Next =>"], 2);
    }
 
    listen( integer channel, string name, key id, string message ) {
        if(message == "<= Back") {
            llDialog(id, "Please browse the available items:\n", mBwd(), channel);
            return;
        }
 
        if(message == "Next =>") {
            llDialog(id, "Please browse the available items:\n", mFwd(), channel);
            return;
        }
        integer itra = llGetInventoryNumber(GIVE_TYPE)-1;
        do {
            if(llGetSubString(message, 0, 23) == llGetSubString(llGetInventoryName(GIVE_TYPE, itra), 0, 23)) {
                llGiveInventory(id, llGetInventoryName(GIVE_TYPE, itra));
                jump close;
            }
        } while(--itra>=0);
@close;
        llListenRemove(_comHandle);
    }
    touch_start(integer total_number)
    {
        if(!~llListFindList(previous_users, (list)llDetectedKey(0))) {
            previous_users += llDetectedKey(0);
            jump ok;
        }
        llInstantMessage(llDetectedKey(0), "You already have received an object.");
        return;
@ok;
        integer comChannel = ((integer)("0x"+llGetSubString((string)llGetKey(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        _comHandle = llListen(comChannel, llDetectedName(0), llDetectedKey(0), "");
        llDialog(llDetectedKey(0), "Please browse the available items:\n", _cList, comChannel);
    }
}

This is not SIM crash or restart resistant. For a permanent solution where you store the avatars that used the object, you will need some external storage mechanism.

Code variation: One object / avatar with Notecard

This variant requires you to have a notecard in the primitive's inventory containing avatar key x object name pairs. For example, the following configuration:

dbd87522-9078-4666-adf3-9b11caccba2da#Gravestone Dropper
1be55407-a792-476d-a5e3-19907f0a02bf#Toilet Dropper

will give the Gravestone Dropper object to the avatar with the key dbd87522-9078-4666-adf3-9b11caccba2da and the Toilet Dropper object to the avatar with the key 1be55407-a792-476d-a5e3-19907f0a02bf.

//////////////////////////////////////////////////////////
// [K] Kira Komarov - 2011, License: GPLv3              //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
key nQuery = NULL_KEY;
integer nLine = 0;
list nList = [];
//pragma inline
string nName = "Presents";
 
default
{
    state_entry()
    {
        integer itra;
        for(itra=0, nList=[]; itra<llGetInventoryNumber(INVENTORY_NOTECARD); ++itra) {
            if(llGetInventoryName(INVENTORY_NOTECARD, itra) == nName)
                jump found_notecard;
        }
        llOwnerSay("Failed to find notecard in inventory...");
        return;
@found_notecard;
        llSetTimerEvent(5.0);
        llOwnerSay("Reading notecard...");
        nQuery = llGetNotecardLine(nName, nLine);
    }
    timer() {
        llSetTimerEvent(0);
        if(!llGetListLength(nList)) {
            nName = "Timeout reading the notecard, are you sure you have a blank line after the data?";
            state fault;
            return;
        }
    }
    touch_start(integer total_number)
    {
        integer itra=llGetListLength(nList)-2;
        do {
            if(llList2Key(nList,itra) == llDetectedKey(0)) {
                llGiveInventory(llDetectedKey(0), llList2String(nList, itra+1));
            }
            itra-=2;
        } while(itra > -1);
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
    on_rez(integer param) {
        llResetScript();
    }
    dataserver(key id, string data) {
        if(id != nQuery) return;
        if(data == EOF) {
            integer itra=llGetInventoryNumber(INVENTORY_OBJECT)-1;
            list objects = [];
            do {
                objects += llGetInventoryName(INVENTORY_OBJECT, itra);
            } while(~--itra);
            itra=llGetListLength(nList)-2;
            do {
                if(llList2Key(nList,itra)) if(!~llListFindList(objects, (list)llList2String(nList, itra+1)) ) {
                    nName = "One item was not found in the inventory, please check the notecard...";
                    state fault;
                    return;
                }
                itra-=2;
            } while(itra > -1);
            llOwnerSay("Notecard read and checked, all is ok!");
            return;
        }
        if(data == "") jump next_line;
        nList += llParseString2List(data, ["#"], []);
@next_line;
        nQuery = llGetNotecardLine(nName, ++nLine);
    }
}
 
state fault
{
    state_entry() {
        llOwnerSay(nName);
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) llResetScript();
    }
}

Hacking

To use the script, create a prim and add objects, notecards, or whatever you want people to be able to get from the prim. After that, drop this script in the prim. You can configure the script easily, here are some pointers for some cosmetic changes you might want to make to the script:

  • The back and forward buttons for the dialog are named in the code "<= Back" and "Next =>" respectively. If you want to change those to something more fancy, just replace every instance of "<= Back" and "Next =>" with your desired text.
  • The dialog text is set to "Please browse the available items:\n". Again, if you wish to enter your own text, just replace every instance of "Please browse the available items:\n" with what you want the dialog to say.
  • The script currently works for everybody who touches it. If you wish to change this and allow only the owner to be able to interact with it, simply replace all instances of llDetectedKey(0) with llGetOwner().
Personal tools