Limit Vendor

From Second Life Wiki
Jump to: navigation, search
KBnote.png Note: Due to licensing issues, all contributions to this wiki have stopped and the articles that we posted are just being maintained. This is one of the projects that has gone further and for updates you are cordially invited to the project page on our wiki.

ChangeLog

10-December-2011

Introduction

I was asked for a vendor that only dispenses a certain amount of items. I made this script so that it should support any number of items and also keeps tracks of limits per items in the primitive's inventory. This uses the Giver script I created in order to generate menu items with forward- and back-buttons.

Setup

  • Drop the items you want to be sold in the vendor primitive.
  • Create a notecard called Vendor Items in order to list the items, prices and limits per item. For example, a vendor with two items, could be added into the Vendor Items notecard:
Super Item#50#3
Great Item#60#2

where the first field before the hash, "Super Item" is the item name in the primitive's inventory, the second field after the hash "50" represents the price for that item and the last field "3" represents the amount of items that the vendor will dispense before it is out of stock. For the second item, it is called "Great Item", costs l$60 and the vendor will dispense two of them before it says it is out of stock.

  • Now, create a script and copy the code below. Configure the script to your liking in the CONFIGURATION section and then drop this script in the vendor.

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 ------------------ //
// This message is shown when the dialog pops up allowing
// a customer to select the product number.
string DIALOG_MESSAGE = "Please choose a number corresponding to the items on the main chat in order to purchase that item. You can use the back and forward buttons to choose the product number. The dialog will time out in 30 seconds. If that happens, please just touch the vendor again. Thank you! Happy Shopping!";
// This is the message that the vendor will send to the 
// customer in case the product is out of stock.
string OUT_OF_STOCK_MESSAGE = "Sorry, that item is out of stock. Try again later.";
// This is a message that is sent to a customer right before
// the script lists all the items in the vencor.
string BUYER_TOUCH_MESSAGE = "This vendor contains the following items:";
 
//////////////////////////////////////////////////////////
// --------------------- INTERNALS -------------------- //
// Channels
integer userHandle = 0;
integer userChannel = 0;
 
// Inventory 
list invLimit = [];
list invItems = [];
list invPrice = [];
 
// Notecard
key iQuery = NULL_KEY;
integer iLine = 0;
integer readNotecard = 0;
 
// Menu
list storage = [];
integer runner = 0;
list cList = [];
 
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));
}
 
// Buyer temporary.
key tmpMenuBuyer;
key tmpBuyer;
integer tmpItemID;
 
default
{
    state_entry()
    {
        integer itra;
        for(itra=0; itra<llGetInventoryNumber(INVENTORY_NOTECARD); ++itra) {
            if(llGetInventoryName(INVENTORY_NOTECARD, itra) == "Vendor Items")
                jump found_items;
        }
        return;
@found_items;
        invItems = [];
        invLimit = [];
        invPrice = [];
        llOwnerSay("Reading vendor notecard, please wait...");
        llSetTimerEvent(5.0);
        readNotecard = 0;
        iQuery = llGetNotecardLine("Vendor Items", iLine);
 
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY)
            llResetScript();
    }
 
    timer() {
        if(readNotecard) {
            llSetTimerEvent(0.0);
            llOwnerSay("Notecard read.");
            runner = 0;
            storage = [];
            cList = [];
            integer item_number = llGetListLength(invItems);
            if(llGetListLength(invPrice) != item_number || llGetListLength(invLimit) != item_number) {
                llOwnerSay("The configuration lines in the Vendor Items notecard seem to be wrong. Please check them again.");
                return;
            }
            integer itra;
            for(itra=0; itra<llGetListLength(invItems); ++itra) {
                storage += (string)(itra+1);
            }
            cList = llListInsertList(llListInsertList(llList2List(storage, runner, runner+=9), ["<= Back"], 0), ["Next =>"], 2);
            llOwnerSay("Requesting permissions.");
            llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
            state vendor;
        }
        llSetTimerEvent(5.0);
    }
 
    run_time_permissions(integer perm) {
        if(perm & PERMISSION_TRIGGER_ANIMATION) {
            llOwnerSay("Vendor becoming active.");
            state vendor;
        }
    }
 
    dataserver(key id,string data) {
        if(id != iQuery) return;
        if(data == EOF) {
            readNotecard = 1;
            return;
        }
        if(data == "") jump next_line;
        list invList = llParseString2List(data, ["#"], [""]);
        invItems += llList2String(invList, 0);
        if(~llListFindList(invPrice, (list)llList2String(invList, 1))) {
            llOwnerSay("Two items cannot have an identical price. Please check your Vendor Items notecard.");
            return;
        }
        if(llList2Integer(invList, 1) < 0) {
            llOwnerSay("The prices in the Vendor Items notecard can only be whole positive numbers. Please check your Vendor Items notecard.");
            return;
        }
        invPrice += llList2Integer(invList, 1);
        invLimit += llList2Integer(invList, 2);
@next_line;
        iQuery = llGetNotecardLine("Vendor Items", ++iLine);
    }
}
 
state vendor
{
    state_entry() {
        llSetPayPrice(PAY_HIDE, [PAY_HIDE ,PAY_HIDE, PAY_HIDE, PAY_HIDE]);
        tmpMenuBuyer = NULL_KEY;
        llOwnerSay("Vendor is now active and able to process requests.");  
    }
 
    touch_start(integer total_number)
    {
        if(tmpMenuBuyer != NULL_KEY) return;
        tmpMenuBuyer = llDetectedKey(0);
        integer itra;
        llInstantMessage(llDetectedKey(0), BUYER_TOUCH_MESSAGE);
        for(itra=0; itra<llGetListLength(invItems); ++itra) {
            string prod = (string)(itra+1) + ".) " + llList2String(invItems, itra) + " costs L$" + llList2String(invPrice, itra) + ".";
            if(llList2Integer(invLimit, itra) == 0) prod += " (OUT OF STOCK)";
            llInstantMessage(llDetectedKey(0), prod);
        }
        userChannel = ((integer)("0x"+llGetSubString((string)llDetectedKey(0),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
        userHandle = llListen(userChannel, "", llDetectedKey(0), "");
        llSetTimerEvent(10);
        llDialog(llDetectedKey(0), DIALOG_MESSAGE, cList, userChannel);
    }
    timer() {
        llSetTimerEvent(0);
        llListenRemove(userHandle);
        if(tmpMenuBuyer) llInstantMessage(tmpMenuBuyer, "Vendor timeout... Please touch again.");
        tmpMenuBuyer = NULL_KEY;
    }
    listen(integer chan,string name,key id,string mes) {
        if(mes == "<= Back") {
            llSetTimerEvent(10);
            llDialog(id, DIALOG_MESSAGE, mBwd(), userChannel);
            return;
        }
        if(mes == "Next =>") {
            llSetTimerEvent(10);
            llDialog(id, DIALOG_MESSAGE, mFwd(), userChannel);
            return;
        }
        llListenRemove(userChannel);
        llInstantMessage(id, "The price for that item is: L$" + llList2String(invPrice, (integer)mes-1) + ". Please right click the vendor and select pay that ammount to receive your item.");
        tmpItemID = (integer)mes-1;
        tmpBuyer = id;
        state payment;
    }
}
 
state payment
{
    state_entry() {
        llSetPayPrice(llList2Integer(invPrice, tmpItemID), [llList2Integer(invPrice, tmpItemID) ,PAY_HIDE, PAY_HIDE, PAY_HIDE]);
        llSetTimerEvent(30);
    }
    timer() {
        llInstantMessage(tmpBuyer, "Sorry, the vendor payment timed out...");
        state vendor;
    }
    money(key id, integer amount) {
        if(id == tmpBuyer) jump buyer_ok;
        llGiveMoney(id, amount);
        llInstantMessage(id, "Sorry, I'm busy with another transaction... Please wait...");
        return;
@buyer_ok;
        if(~llListFindList(invPrice, (list)amount)) jump found_item;
        llGiveMoney(id, amount);
        llInstantMessage(id, "Sorry, that price is wrong.");
        return;
@found_item;
        if(llList2Integer(invLimit, llListFindList(invPrice, (list)amount)) > 0) jump limit_ok;
        llInstantMessage(id, OUT_OF_STOCK_MESSAGE);
        llGiveMoney(id, amount);
        return;
@limit_ok;
        llGiveInventory(id, llList2String(invItems, llListFindList(invPrice, (list)amount)));
        invLimit = llListReplaceList(invLimit, (list)(llList2Integer(invLimit, llListFindList(invPrice, (list)amount))-1), llListFindList(invPrice,(list)amount), llListFindList(invPrice,(list)amount));
        llOwnerSay(llDumpList2String(invLimit, "\n"));
        state vendor;
    }
}