backpack.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
integer wasMenuIndex = 0;
list wasDialogMenu(list input, list actions, string direction) {
    integer cut = 11-wasListCountExclude(actions, [""]);
    if(direction == ">" &&  (wasMenuIndex+1)*cut+wasMenuIndex+1 < llGetListLength(input)) {
        ++wasMenuIndex;
        jump slice;
    }
    if(direction == "<" && wasMenuIndex-1 >= 0) {
        --wasMenuIndex;
        jump slice;
    }
@slice;
    integer multiple = wasMenuIndex*cut;
    input = llList2List(input, multiple+wasMenuIndex, multiple+cut+wasMenuIndex);
    input = wasListMerge(input, actions, "");
    return input;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
integer wasListCountExclude(list input, list exclude) {
    if(llGetListLength(input) == 0) return 0;
    if(llListFindList(exclude, (list)llList2String(input, 0)) == -1) 
        return 1 + wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
    return wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
list wasListMerge(list l, list m, string merge) {
    if(llGetListLength(l) == 0 && llGetListLength(m) == 0) return [];
    string a = llList2String(m, 0);
    if(a != merge) return [ a ] + wasListMerge(l, llDeleteSubList(m, 0, 0), merge);
    return [ llList2String(l, 0) ] + wasListMerge(llDeleteSubList(l, 0, 0), llDeleteSubList(m, 0, 0), merge);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
wasDescriptionSet(string var, string val) {
    list dVars = llParseString2List(llGetObjectDesc(), ["&", "="], []);
    list set = [];
    do {
        string dVar = llList2String(dVars, 0);
        if(dVar == var) {
            if(llListFindList(set, (list)(dVar + "=" + val)) != -1) jump continue;
            set += dVar + "=" + val;
            jump continue;
        }
        if(llList2String(dVars, 1) == "") jump continue; 
        set += dVar + "=" + llList2String(dVars, 1);
@continue;
        dVars = llDeleteSubList(dVars, 0, 0);
        dVars = llDeleteSubList(dVars, 0, 0);
    } while(llGetListLength(dVars));
    if(llListFindList(set, (list)(var + "=" + val)) != -1) jump dump;
    set += var + "=" + val;
@dump;
    string desc = llDumpList2String(set, "&");
    // The description field is limited by the specification to 127 bytes.
    if(llStringLength(desc) > 127) {
        llSay(DEBUG_CHANNEL, "Description overflow.");
        return;
    }
    llSetObjectDesc(desc);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasDescriptionGet(string var) {
    list dVars = llParseString2List(llGetObjectDesc(), ["&", "="], []);
    do {
        string dVar = llList2String(dVars, 0);
        if(dVar != var) jump continue;
        return llList2String(dVars, 1);
@continue;
        dVars = llDeleteSubList(dVars, 0, 0);
        dVars = llDeleteSubList(dVars, 0, 0);
    } while(llGetListLength(dVars));
    return "";
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
list wasDescriptionDecode(string data) {
    return llParseString2List(data, ["&", "="], []);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
wasDescriptionDelete(string var) {
    list dVars = llParseString2List(llGetObjectDesc(), ["&", "="], []);
    list set = [];
    do {
        string dVar = llList2String(dVars, 0);
        if(dVar == var) jump continue;
        set += dVar + "=" + llList2String(dVars, 1);
@continue;
        dVars = llDeleteSubList(dVars, 0, 0);
        dVars = llDeleteSubList(dVars, 0, 0);
    } while(llGetListLength(dVars));
    llSetObjectDesc(llDumpList2String(set, "&"));
}
 
default
{
    touch_start(integer num) {
        key owner = llGetOwner();
        if(llDetectedKey(0) != owner) return;
        integer comChannel = (integer)("0x8" + llGetSubString(owner, 0, 6));
        llListen(comChannel, "", owner, "");
        llDialog(llGetOwner(), "Please choose from the list of available medicine in your backpack:\n", wasDialogMenu(llList2ListStrided(wasDescriptionDecode(llGetObjectDesc()), 0, -1, 2), ["<= Back", "[ CHECK ]", "Next =>"], ""), comChannel);
    }
    listen(integer channel, string name, key id, string message) {
        if(message == "<= Back") {
            llDialog(id, "Please choose from the list of available medicine in your backpack:\n", wasDialogMenu(llList2ListStrided(wasDescriptionDecode(llGetObjectDesc()), 0, -1, 2), ["<= Back", "[ CHECK ]", "Next =>"], "<"), channel);
            return;
        }
        if(message == "Next =>") {
            llDialog(id, "Please choose from the list of available medicine in your backpack:\n", wasDialogMenu(llList2ListStrided(wasDescriptionDecode(llGetObjectDesc()), 0, -1, 2), ["<= Back", "[ CHECK ]", "Next =>"], ">"), channel);
            return;
        }
        if(message == "[ CHECK ]") {
            list m = llList2ListStrided(wasDescriptionDecode(llGetObjectDesc()), 0, -1, 2);
            list s = llList2ListStrided(llDeleteSubList(wasDescriptionDecode(llGetObjectDesc()), 0, 0), 0, -1, 2);
            integer i = llGetListLength(m)-1;
            do {
                llOwnerSay("You have " + llList2String(s, i) + " of " + llList2String(m, i));
            } while(--i>-1);
            return;
        }
        integer consume = (integer)wasDescriptionGet(message)-1;
        if(consume == 0) {
            wasDescriptionDelete(message);
            jump say;
        }
        wasDescriptionSet(message, (string)consume);
@say;
        integer comChannel = (integer)("0x8" + llGetSubString(llGetOwner(), 0, 6));
        llSay(comChannel, message);
        llOwnerSay("You take the \"" + message + "\" out of your backpack and use it.");
    }
    on_rez(integer num) {
        llResetScript();
    }
}