Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Library: FOSSL Vending System v1.1 enabling income splitting and more

Fleur Dollinger
Registered User
Join date: 31 Mar 2007
Posts: 8
04-30-2008 18:32
I found the FOSSL set of scripts made by Ilobmirt Tenk when I was looking for options and information on how to create a vendor for my product. I thought it looked pretty good, but I wanted to be able to distribute vendors to other sellers and enable income splitting and so I had to make some modifications to suit my purpose. I tried to retain the customisable and open structure and something of the style used by Ilobmirt. Am posting my results here to say thanks to Ilobmirt.

***************************************
INCOME SPLITTING
***************************************
Scripts required for income splitting
- FOSSLVendorBuyModule (Original version 1 Script modified)
- FOSSL_BUY_SETTINGS (New settings card for the buy module)

Essential Settings
--------------------
split = 1
SPLIT 1 KEY = cc3012aa123some-avatars-key0000-123456
SPLIT 2 KEY = 3ddccc30123some-avatars-key0000-123456
SPLIT 3 KEY = cc2212aa123some-avatars-key0000-123456
SPLIT 1 AMOUNT = 50
SPLIT 3 AMOUNT = 25
SPLIT 2 AMOUNT = 5

NB: you only need to include lines for number of splits. Amounts are percentages unless SPLITS 1-3 FLAT are changed to 1.

Optional
--------------

deed to group = 0

The 'deed to group' default is 0 so this parameter only required in settings card if changing to 1
.. this is NOT recommended and will be reset to 0 if split is set to 1. You would only set deed to group = 1 (which would allow the owner to deed the vendor to a group) if you didn't want to pay anyone from the vendor. Note that this includes not paying your customer a refund if they paid in too much money.

SPLIT 1 FLAT = 1
SPLIT 2 FLAT = 1
SPLIT 3 FLAT = 1

SPLIT 1-3 FLAT default is 0 so only required in settings card if changing to 1. If changed to 1 instead of the AMOUNT being a percentage, it will be a flat figure - eg $10 instead of 10%. Anything left over after splitting is retained by the vendor owner, effectively enabling a 4-way split if necessary.

Does a couple of things
- the new notecard enables you to set up the splits
- the script now disallows 'Deed to Group' by default

This is important because a vendor deeded to a group cannot pay anyone, not even refund overpayments. Thanks to my pal mrBrett for alerting me to this potential pitfall. So deeding the vendor to a group will deactivate it. Although not recommended in any circumstances, this functionality can be altered by changing a couple of settings.

**********************************************
SERVE A SEPARATE INFO CARD FOR EACH PRODUCT
**********************************************
I wanted to be able to serve a separate notecard for each product rather than one notecard per vendor. This required a few more changes:

- FOSSLVendorMainClient (Original Version 1 script modified)
- FOSSL_CLIENT_SETTINGS (additional parameters)
separate info = 1
info suffix = SomeText

**********************************************
GIVE AWAY YOUR VENDORS TO OTHER SELLERS
**********************************************
One more thing which wasn't obvious to me at first, if you want to be able to give away your no-modify vendors to other sellers so they can sell your products on your behalf, and you're using the server version of FOSSL you also have to make a change to the settings card for the server.

- FOSSL_SERVER_SETTINGS
anon access = 1

Since there were some warnings associated with changing this setting, I have also added a script to my vendors which allows me to send an individual one a message and kill it if necessary :) This is dependent on them telling me when they're rezzed. Since this part of the scripted solution isn't as elegant or complete as the FOSSL mods I haven't included them here.


See replies for scripts
Fleur Dollinger
Registered User
Join date: 31 Mar 2007
Posts: 8
FOSSLVendorBuyModule Revised to enable income splitting
04-30-2008 18:34
CODE

//FOSSLVendorBuyModule
//---------------------------------------------------------------------------------//
//Copyright Info Below... Please Do not Remove //
//---------------------------------------------------------------------------------//

//(c)2007 Ilobmirt Tenk

//This file is part of the FOSSL Vendor Project

// FOSSL Vendor is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.

// FOSSL Vendor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

//---------------------------------------------------------------------------------//
//Copyright Info Above... Please Do not Remove //
//---------------------------------------------------------------------------------//
// modified by Fleur Dollinger 6 April 2008 - enable income splitting
// modified by Fleur Dollinger 28 April 2008 - option to disable deed to group

//=================================================================//
//List all the global variables here...
//=================================================================//

integer intCurrentPrice = 0;


integer intListenChannel = 40;


//count of the current Notecard line to read
integer intSettingLines = 0;

//key of the notecard line to be read
key keySettingsId;

//determines if this object displays debugging output
integer blnDebug = FALSE;

//income can be split between up to 3 avatars (as well as owner) make split=1 and set splits & keys in notecard
integer split = FALSE;

//3 separate splits allows up to 4-way split if vendor owner is not one of the 3 keys
key split1 = NULL_KEY;
key split2 = NULL_KEY;
key split3 = NULL_KEY;

//change to TRUE to pay a flat amount instead of a percentage
integer split1flat = FALSE;
integer split2flat = FALSE;
integer split3flat = FALSE;

// if split1flat = FALSE, amount is percentage, otherwise it is a $ amount
float split1amount;
float split2amount;
float split3amount;

//DEFAULT IS TO DISABLE DEED TO GROUP
// note that that the vendor will not be able to refund overpayments or split and distribute money if the vendor is deeded to group so its recommended to always leave this disabled
integer intDeedToGroup = FALSE;
//get the object's owner - NULL_KEY
list groupD;

//This displays messages to the owner if debug mode has been set to true
debugMessage(string strMessage){

if(blnDebug == TRUE){

llOwnerSay("FOSSLVendorBuy: " + strMessage);

}
}


//if SPLIT=1
splitMoney(integer amount)
{
if (split1 != NULL_KEY && split1 != llGetOwner() && split1 != "")
{
if (split1flat == TRUE)
{
if (amount > split1amount)
{
llGiveMoney(split1, (integer) split1amount);

}
} else
{
if ((integer) (split1amount*amount/100) >= 1)
{
llGiveMoney(split1,(integer) (amount*split1amount/100));
}
}
}

if (split2 != NULL_KEY && split2 != llGetOwner() && split2 != "")
{
if (split2flat == TRUE)
{
if (amount > split2amount)
{
llGiveMoney(split2, (integer) split2amount);
}
} else
{
if ((integer) (split2amount*amount/100) >= 1)
{
llGiveMoney(split2,(integer) (amount*split2amount/100));
}
}
}

if (split3 != NULL_KEY && split3 != llGetOwner() && split3 != "")
{
if (split3flat == TRUE)
{
if (amount > split3amount)
{
llGiveMoney(split3, (integer) split3amount);
}
} else
{
if ((integer) (split1amount*amount/100) >= 1)
{
llGiveMoney(split3,(integer) (amount*split3amount/100));
}
}
}


}

//In this state, vendor must accept debit permissions before using the vendor
default
{

state_entry()
{

groupD = llGetObjectDetails(llGetOwner(), [OBJECT_GROUP]);
debugMessage("GroupD:" + (string) llList2String(groupD,0));

//request permission on startup
if ((intDeedToGroup == 0 && (key) llList2String(groupD,0) == NULL_KEY) || intDeedToGroup == 1)
{
llRequestPermissions(llGetOwner(),PERMISSION_DEBIT); //Asks store owner for the ability to sell things.
}
else
{
llWhisper(0,"Deeding this vendor to a group is not permitted. Transaction permission has been revoked. I hope you didn't really want to steal $$ from the creator of these products.");
}
}


on_rez(integer start_param)
{

//reset script upon rezz
llResetScript();

}



listen( integer channel, string name, key id, string message ){

if(id == llGetOwner()){


//Enable or disable verbose output
if(message == "debug"){

blnDebug = !blnDebug;
llOwnerSay("FOSSLVendorBuy: Debug Mode set to: " + (string) blnDebug);
llMessageLinked(LINK_SET,0,"debugMode#" + (string) blnDebug,NULL_KEY);


}
}
}


link_message(integer sender_number, integer number, string message, key id)
{

//Separate the linked message into the function and variables
list lstMessage = llParseString2List(message,["#"],[]);
string strFunction = llList2String(lstMessage,0);

if(message == "goingOnline"){

//prevent vendor from staying online untill permissions are granted
llMessageLinked(LINK_SET,0,"goOffline",NULL_KEY); //Tell Vendor to go offline if not already


}
//debugMode#blnEnabled
else if(strFunction == "debugMode"){

blnDebug = (integer) llList2String(lstMessage,1);

}


}

//Depending on if avatar accepts or declines request, either stay in this state, or move on...
run_time_permissions(integer permissions)
{

//if user accepted debit permissions
if(permissions & PERMISSION_DEBIT){

llMessageLinked(LINK_SET,0,"goOnline",NULL_KEY); //Tell Vendor to go online if not already
state transactionsAcceptable; //Change state to allow for transactions



}
else if(!(permissions & PERMISSION_DEBIT)){

llOwnerSay("Sorry, but this vendor requires its owner to allow for debit permissions.");
llOwnerSay("To enable vendor service, touch the containing prim of the payment script, and accept the debit request.");

}

}

touch(integer total_number)
{

if ((intDeedToGroup == 0 && (key) llList2String(groupD,0) == NULL_KEY) || intDeedToGroup == 1)
{
llRequestPermissions(llGetOwner(),PERMISSION_DEBIT); //Asks store owner for the ability to sell things.
} else
{
llWhisper(0,"Transaction permission has been denied/revoked for this vendor.");
}

}

}

//In this state, vendor has accepted debit permissions
//Allow transactions to commence
state transactionsAcceptable
{

state_entry(){

llOwnerSay("Vendor is now able to make transactions with this script.");
//Obtain the first line of the vendor link settings;
keySettingsId = llGetNotecardLine("FOSSL_BUY_SETTINGS", intSettingLines);


}

changed( integer vBitChanges ){
//detect change of owner on the ground, as this may indicate that the owner has deeded this object to the group, in which case we may want to disable transactions.
//If any kind of split is activated you will have to disallow deeding this vendor to the group as groups cannot 'pay' other ppl out of the split.
//This is really only relevant if you are giving this vendor to other people to sell your products for you.
if (vBitChanges & CHANGED_OWNER){

llResetScript();
}
}


on_rez(integer start_param){

llResetScript();

}

//set the current price to the value of num. This is incase that the user over/under pays for the item
link_message(integer sender_num, integer num, string str, key id){

if(str == "updatePrice"){

intCurrentPrice = num;

}

}

//Whenever an avatar pays an object that this script is in, check for correct amount.
//Then if correct amount is payed, notify the system of a sale
money(key giver, integer amount) {

// has the user paid the correct amount?
if (amount == intCurrentPrice)
{
// if so, thank the payer by name.
llSay(0,"Thank you, " + llKey2Name(giver) + ".");
//give them the lovely object displayed on the vendor

//send message back to root object to give out product
llMessageLinked(LINK_ROOT,0,"itemBought#" + (string)giver,NULL_KEY);

// if settings card includes split details
if (split == TRUE)
{
splitMoney (amount);
}
}

// is the amount paid less than it needs to be?
else if (amount < intCurrentPrice)
{
// if so, tell them they're getting a refund, then refund their money.
llSay(0,"You didn't pay enough, " + llKey2Name(giver) + ". Refunding your payment of L$" + (string)amount + ".");
llGiveMoney(giver, amount); // refund amount paid.
}

// if it's not exactly the amount required, and it's not less than the amount required,
// the payer has paid too much.
else
{
// tell them they've overpaid.
integer intRefund = amount - intCurrentPrice; // determine how much extra they've paid.
llSay(0,"You paid too much, " + llKey2Name(giver) + ". Your change is L$" + (string)intRefund + ".");
llGiveMoney(giver, intRefund); // refund their change.
//give them the lovely object displayed on the vendor

//send message back to root object to give out product.
llMessageLinked(LINK_ROOT,0,"itemBought#" + (string)giver,NULL_KEY);

}

}




//Read the link settings notecard. Configure the link based on each line.
dataserver(key query_id, string data) {

if (query_id == keySettingsId) {
if (data != EOF) { // not at the end of the notecard

debugMessage("Notecard Line = \"" + data + "\"");
//split the record into variable names and values
list lstSplit = llParseString2List(data,["="],[]);
string strVariable = llToUpper(llStringTrim(llList2String(lstSplit,0),STRING_TRIM));
string strValue = llStringTrim(llList2String(lstSplit,1),STRING_TRIM);

// get setting for intDeedToGroup (default is 0 = disable)
if(strVariable == "DEED TO GROUP"){

intDeedToGroup = (integer) strValue;
debugMessage("intDeedToGroup = " + (string) split);
}

// get settings for splits
if(strVariable == "SPLIT"){

split = (integer) strValue;
//if income splitting is enabled, must disable Deed To Group
if (split == 1)
{ intDeedToGroup = 0;
debugMessage("DEED TO GROUP DISABLED AS INCOME SPLITTING HAS BEEN ACTIVATED");
};

debugMessage("SPLIT = " + (string) split);
}
if(strVariable == "SPLIT 1 KEY"){

split1 = (key) strValue;
debugMessage("split1 = " + (string) split1);


}
if(strVariable == "SPLIT 2 KEY"){

split2 = (key) strValue;
debugMessage("split2 = " + (string) split2);

}
if(strVariable == "SPLIT 3 KEY"){

split3 = (key) strValue;
debugMessage("split3 = " + (string) split3);

}
if(strVariable == "SPLIT 1 FLAT"){

split1flat = (integer) strValue;
debugMessage("split1flat = " + (string) split1flat);


}
if(strVariable == "SPLIT 2 FLAT"){

split2flat = (integer) strValue;
debugMessage("split2flat = " + (string) split2flat);

}
if(strVariable == "SPLIT 3 FLAT"){

split3flat = (integer) strValue;
debugMessage("split3flat = " + (string) split3flat);

}
if(strVariable == "SPLIT 1 AMOUNT"){

split1amount = (float) strValue;
debugMessage("split1amount = " + (string) split1amount);


}
if(strVariable == "SPLIT 2 AMOUNT"){

split2amount = (float) strValue;
debugMessage("split2Amount = " + (string) split2amount);

}
if(strVariable == "SPLIT 3 AMOUNT"){

split3amount = (float) strValue;
debugMessage("split3amount = " + (string) split3amount);

}
if(strVariable == "CHANNEL"){

intListenChannel = (integer) strValue;
debugMessage("intListenChannel = " + (string) intListenChannel);

}

keySettingsId = llGetNotecardLine("FOSSL_BUY_SETTINGS", ++intSettingLines); // request next line
}
else{


}

}

//Capture toggle of Debug Setings
llListen(intListenChannel,"",llGetOwner(),"debug");

}
}

Fleur Dollinger
Registered User
Join date: 31 Mar 2007
Posts: 8
FOSSLVendorMainClient Pt1 revised to enable separate info cards per product
04-30-2008 18:38
CODE

//FOSSLVendorMainClient
//---------------------------------------------------------------------------------//
//Copyright Info Below... Please Do not Remove //
//---------------------------------------------------------------------------------//

//(c)2007 Ilobmirt Tenk

//This file is part of the FOSSL Vendor Project

// FOSSL Vendor is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.

// FOSSL Vendor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

//---------------------------------------------------------------------------------//
//Copyright Info Above... Please Do not Remove //
//---------------------------------------------------------------------------------//

//=================================================================//
//List all the global variables here...
//=================================================================//

string strCurrentItem = ""; //This is the current item that the product index is on
// the server, store the notecards in the server
string strINFO = " Info"; //This is the suffix for the info notecard - create one for each product - if using
integer intSeparateInfo = TRUE; //SET TRUE OR FALSE - if true the vendor will deliver a separate card for each item

list lstItemRequestList; //List of items to be requested from the server
//each record is in the form of "Category#SubCategory#DatabaseKey"

list lstDisplays; //List of displays to render products on
//Each display has a record of "Link#absoluteCategory#Category#absoluteSubCategory#SubCategory"

integer intHashCounter = -2147483646; //Used in the creation of a unique request

integer intSettingLines = 0; //Total # of lines in the settings notecard

integer intProductLines = 0; //Total # of lines in the products notecard

integer intItemRequestList = 0; //Number of item database requests to send to the server

integer intProductIndexCategory = 0; //Current index of visible product shown by Category

integer intProductIndexSubCategory = 0; //Current index of visible product shown by sub Category

integer intProductIndexMaxCategory = 0; //Maximum index of categorys

integer intProductIndexMaxSubCategory = 0; //Maximumindex of SubCategorys

integer intInfoCount = 0; //Number of times people clicked on the info button

integer intEmailRequestStarted = 0; //Last time an e-mail was sent out to the server

integer intLastProductUpdate = 0; //Last time product list was updated

key keySettingsId; //Used to load system Settings

key keyProductsUUID; //The uuid of the notecard to be read for products

key keyProductCategories; //The key of the line being read from the products card to determine length of categories

integer blnWaitingForServer = FALSE; //Used to determine if vendor is currently requesting a uuid for the product notecard

integer blnServerDown = TRUE; //Used to determine if the in-world server is down

integer blnDebug = FALSE; //Used to determine if vendor will output debugging information

//=================================================================//
//Customize the following variables
//=================================================================//

key keyTexLoading = "56617727-a972-f7d2-a730-484644a1fcb7"; //Texture UUID to represent that an item is loading

key keyTexOffline = "b8426d53-221d-9fc2-5b29-3ef16e2bd7a3"; //Texture UUID to represent the vendor has no connection to the server

key keyTexNothing = "93481f65-67ff-f166-cc64-e6fe1069c1c1"; //Texture UUID to represent that there is no item for this index

key keyServer = NULL_KEY; //This is a pointer to an object server. If this is not NULL_KEY, the vending system will consider selling items from that server.

string strInfoCardName = ""; //Name of the information notecard that talks about your company

string strProductsCardName = "FOSSL_PRODUCTS_LIST"; //Name of the products card to read off of

string strEncryptionKey = ""; //Key to encrypt its communications with

integer intBuffer = 0; //How big do you want you product inventory to be? This goes beyond the # of displays

float fltInventoryRefresh = 3600.0; //Time delay before vendor clears its product inventory and makes another request for more items

integer intServerTimeout = 60; //Time in seconds before vendor is considered down

integer intCommChannel = 939; //Channel in which you can communicate to your vending machine

integer blnEmailAgents = FALSE; //Can this script export the task of sending e-mails to other in-world objects?

//=================================================================//
//Customize the above variables
//=================================================================//



//=================================================================//
//Define global Functions here...
//=================================================================//

//This displays messages to the owner if debug mode has been set to true
debugMessage(string strMessage){

if(blnDebug == TRUE){

llOwnerSay("FOSSLVendorMainClient: " + strMessage);

}

}

//Used for encrypting/decrypting communications between itself and the inworld server
string crypt(string strPayload){

//If there is an encryption key...
if(strEncryptionKey != ""){

//Use this simple Algrorythm to encrypt/decrypt input string
return llBase64ToString(llXorBase64StringsCorrect(llStringToBase64(strPayload), llStringToBase64(strEncryptionKey)));

}
//otherwise...
else{

//Just return the string as is.
return strPayload;

}

}

//Enters the vending system into an offline mode if not offline already
goOffline(){

blnServerDown = TRUE; //Set state of vendor to be offline
clearRequests(); //Clear any pending requests for products
llMessageLinked(LINK_SET,0,"goingOffline",NULL_KEY); //Notify system its going offline
displaysOffline(); //Reflect the changes on display

}

//Restores functionality to the vending machine from an offline mode
goOnline(){

blnServerDown = FALSE; //Set state of vendor to be online
llMessageLinked(LINK_SET,0,"goingOnline",NULL_KEY);
displaysLoading(); //reflect changes on display

}

//This will set each display to a loading texture
//This will also send out database requests for each link
displaysLoading(){

list lstDisplay = [];
integer intIndex = 0;
integer intDisplayIndex = 0;
integer intDisplays = llGetListLength(lstDisplays);
integer blnDisplayAbsoluteCategory = 0;
integer blnDisplayAbsoluteSubCategory = 0;
integer intDisplayCategory = 0;
integer intDisplaySubCategory = 0;
integer intDisplayRelativeCategory = 0;
integer intDisplayRelativeSubCategory = 0;

strCurrentItem = "";
updatePrices();

//Loop through each index of the available displays
for(; intIndex < intDisplays; intIndex++){

//collect prim location, then texturethem with the offline texture
//Link#absoluteCategory#Category#absoluteSubCategory#SubCategory
lstDisplay = llParseString2List(llList2String(lstDisplays,intIndex),["#"],[]);
intDisplayIndex = (integer) llList2String(lstDisplay,0);

intDisplayRelativeCategory = (integer) llList2String(lstDisplay,2);
blnDisplayAbsoluteCategory = (integer) llList2String(lstDisplay,1);
blnDisplayAbsoluteSubCategory = (integer) llList2String(lstDisplay,3);
intDisplayRelativeSubCategory = (integer) llList2String(lstDisplay,4);

//Obtain displayed category
if(blnDisplayAbsoluteCategory == TRUE){

intDisplayCategory = intDisplayRelativeCategory;
debugMessage("Display category is Absolute");

}
else{

intDisplayCategory = intDisplayRelativeCategory + intProductIndexCategory;
debugMessage("Display category is Relative");

}

debugMessage("Normalising Category index");
//Make sure displayed Category is between 0 and the maximum category
while(intDisplayCategory > intProductIndexMaxCategory){

intDisplayCategory -= (intProductIndexMaxCategory + 1);

}

while(intDisplayCategory < 0){

intDisplayCategory += (intProductIndexMaxCategory + 1);

}

//Obtain displayed SubCategory
if(blnDisplayAbsoluteSubCategory == TRUE){

intDisplaySubCategory = intDisplayRelativeSubCategory;
debugMessage("Display subcategory is Absolute");

}
else{

intDisplaySubCategory = intDisplayRelativeSubCategory + intProductIndexSubCategory;
debugMessage("Display subcategory is relative");

}

//Make sure displayed SubCategory is between 0 and the maximum category
while(intDisplaySubCategory > intProductIndexMaxSubCategory){

intDisplaySubCategory -= (intProductIndexMaxSubCategory + 1);

}

while(intDisplaySubCategory < 0){

intDisplaySubCategory += (intProductIndexMaxSubCategory + 1) ;

}

debugMessage("Display Categories found @ " + (string) intDisplayCategory + "#" + (string) intDisplaySubCategory);


llSetLinkTexture(intDisplayIndex, keyTexLoading ,ALL_SIDES);

debugMessage("Item Request for link # "+ (string) intDisplayIndex +" = " + (string) intDisplayCategory + "#" + (string) intDisplaySubCategory);
itemRequest(intDisplayCategory,intDisplaySubCategory,intDisplayIndex);

}

}

//This will Update all visible displays with the offline texture
//Also, it will prevent payments to be made to the machine
displaysOffline(){

list lstDisplay = [];
integer intIndex = 0;
integer intDisplayIndex = 0;
integer intDisplays = llGetListLength(lstDisplays);

strCurrentItem = "";
updatePrices();

//Loop through each index of the available displays
for(; intIndex < intDisplays; intIndex++){

//collect prim location, then texturethem with the offline texture
lstDisplay = llParseString2List(llList2String(lstDisplays,intIndex),["#"],[]);
intDisplayIndex = (integer) llList2String(lstDisplay,0);
llSetLinkTexture(intDisplayIndex, keyTexOffline ,ALL_SIDES);

}

}

//This will update a single screen with an associated texture
updateDisplay(string strReturnQuery){

debugMessage("strReturnQuery for texturing = " + strReturnQuery);
//Extract the query into its elements
//return query = "dbResults#ReturnQuery#(productRecord)"
//ProductRecord = "Category#Subcategory#Price#Name#TextureUUID"
list lstReturnQuery = llParseString2List(strReturnQuery,["#"],[]);

//extract the query ID into its elements
//queryID = "linkNumber^queryHash"
list lstQuery = llParseString2List(llList2String(lstReturnQuery,1),["#"],[]);
string strTextureElement = llList2String(lstReturnQuery,6);

integer intLinkNumber = (integer) llList2String(lstQuery,0);
debugMessage("Texture for Prim #"+ (string) intLinkNumber +" = " + strTextureElement );

//Granted, the image for the item index is available, texure it.
if( (strTextureElement != "") && (strTextureElement != "NO_IMAGE_AVAILABLE") ){

llSetLinkTexture(intLinkNumber,(key) strTextureElement,ALL_SIDES);

}
//If there doesn't appear to be an image for the product, give it the nothing texture
else{

llSetLinkTexture(intLinkNumber,keyTexNothing,ALL_SIDES);

}

//If query returned something that is equal to product index, capture it as a current index
if((llList2String(lstReturnQuery,2) + "#" + llList2String(lstReturnQuery,3)) == ((string)intProductIndexCategory + "#" + (string)intProductIndexSubCategory )){

strCurrentItem = llDumpList2String(llList2List(lstReturnQuery,2,-1),"#");
//change the info card name
if (intSeparateInfo == TRUE)
{
// use the product name plus the strINFO suffix to identify the notecard to deliver;
strInfoCardName = llList2String(llList2List(lstReturnQuery,2,-1), 3) + strINFO;
};
//NB if FALSE the originally set info card wil be used instead;

debugMessage("Current Info name:" + strInfoCardName);
//Update prices
updatePrices();

}

}


//This makes the vendor update the price based upon current Index as well as the displayed objects.
updatePrices(){

list lstParsedItem = [];
integer intPrice = 0;

if(strCurrentItem != ""){

lstParsedItem = llParseString2List(strCurrentItem,["#"],[]);
intPrice = (integer) llList2String(lstParsedItem,2);

if((intPrice > 0) && (blnServerDown == FALSE) ){

llSetPayPrice(PAY_HIDE, [intPrice, PAY_HIDE, PAY_HIDE, PAY_HIDE]);

}
else{

llSetPayPrice(PAY_HIDE, [PAY_HIDE, PAY_HIDE, PAY_HIDE, PAY_HIDE]);

}

llMessageLinked(LINK_SET,intPrice,"updatePrice",NULL_KEY); //set the prices for the buy/gift buttons

}
else{

llSetPayPrice(PAY_HIDE, [PAY_HIDE, PAY_HIDE, PAY_HIDE, PAY_HIDE]);

}

}

//This function adds item requests to the buffer before it starts obtaining them from the database
itemRequest(integer intCategory, integer intSubCategory, integer intLink){

//keep hash counter going. Never let it reach its end
if(intHashCounter == 2147483646 ){

intHashCounter = -2147483646;

}

string strQueryID = (string)intLink + "^" + llMD5String("H@5h",++intHashCounter);

//take that record and add it to the waitlist
lstItemRequestList = llListReplaceList((lstItemRequestList = []) + lstItemRequestList,[(string)intCategory + "#" + (string)intSubCategory + "#" + strQueryID ],intItemRequestList,intItemRequestList);

//increment # of items on the waitlist
intItemRequestList++;

llMessageLinked(LINK_SET,0,"dbRequest#" + strQueryID + "#" + (string) intCategory + "#" + (string) intSubCategory ,NULL_KEY);

}

//This function removes item requests by their index
removeItemRequest(integer intIndex){

//remove the item by index
lstItemRequestList = llDeleteSubList((lstItemRequestList = []) + lstItemRequestList,intIndex,intIndex);

//decrement # of items on the waitlist
intItemRequestList--;


}

//clears the request buffer
clearRequests(){

lstItemRequestList= [];
intItemRequestList = 0;

}

//Proccesses the dbQuery to see if it matches a request
processQuery(string strDBQuery){

integer intIndex = -1;
integer blnFound = FALSE;
string strQueryID = llList2String(llParseString2List(strDBQuery,["#"],[]),1);
list lstParsedRequest = [];

debugMessage("FOSSLVendorClient: Proccessing Query = " + strQueryID );

//loop while there are requests to proccess and if the query doesn't match
do{

++intIndex; //advance an index

lstParsedRequest = llParseString2List(llList2String(lstItemRequestList,intIndex),["#"],[]);

if(strQueryID == llList2String(lstParsedRequest,2) ){

blnFound = TRUE;

updateDisplay(strDBQuery);

removeItemRequest(intIndex);

}



}while( (intIndex < intItemRequestList) && (blnFound == FALSE) );

}

default
{
//Activated upon the beginning of script execution
//Start setting up vendor settings
state_entry()
{

//Keep debug mode in all scripts the same on startup or reset
llMessageLinked(LINK_SET,0,"debugMode#"+ (string) blnDebug ,NULL_KEY);
llOwnerSay("FOSSLVendorClient: Loading Settings...");
keySettingsId = llGetNotecardLine("FOSSL_CLIENT_SETTINGS", intSettingLines); //Obtain the first line of the vendor settings
//llRequestPermissions(llGetOwner(),PERMISSION_DEBIT); //Asks store owner for the ability to sell things.
}

//Activated upon rezzing of its container prim
//Use this event to update the settings of the vending system
on_rez(integer start_param){

//reset the script when I rez it
llResetScript();

}

//Activated upon modification of vendor's inventory
//Reset script to make use of possible changes
changed(integer change){

// If I change the contents of the vendor
if(change & CHANGED_INVENTORY){

//reset the script to make use of the changes
llResetScript();

}

}

//Activate this event when a companion script sends out a linked message
//Use this event to determine if that script is making a request or response of this script
//This script accepts requests to go online of offline, link clicked requests,
//screen addition requests, buying requests, and info requests
link_message(integer sender_num, integer num, string str, key id){

debugMessage("Link message recieved = " + str);

list lstMessage = llParseString2List(str,["#"],[]);
string strFunction = llList2String(lstMessage,0);

//Vendor module wants vendor to go online
if(strFunction == "goOnline"){

goOnline();

}
//Vendor module wants vendor to go offline
if(strFunction == "goOffline"){

goOffline();

}
//"linkClicked#absoluteCategory#CategoryShift#absoluteSubcategory#SubCategoryShift"
if(strFunction == "linkClicked"){

integer intDiffCategory = (integer) llList2String(lstMessage,2);
integer intDiffSubCategory = (integer) llList2String(lstMessage,4);
integer blnAbsoluteCategory = (integer) llList2String(lstMessage,1);
integer blnAbsoluteSubCategory = (integer) llList2String(lstMessage,3);

//Determine Category
if(blnAbsoluteCategory == TRUE){

intProductIndexCategory = intDiffCategory;

}
else{

intProductIndexCategory += intDiffCategory;

}

//Determine Subcategory
if(blnAbsoluteSubCategory == TRUE){

intProductIndexSubCategory = intDiffSubCategory;

}
else{

intProductIndexSubCategory += intDiffSubCategory;

}

//Keep Category larger than 0
while(intProductIndexCategory < 0){

intProductIndexCategory += (intProductIndexMaxCategory + 1);

}

//Keep SubCategory larger than 0
while(intProductIndexSubCategory < 0){

intProductIndexSubCategory += (intProductIndexMaxSubCategory + 1);

}

//Keep Category less than or equal to max
while(intProductIndexCategory > intProductIndexMaxCategory){

intProductIndexCategory -= (intProductIndexMaxCategory+1);

}

//Keep SubCategory less than or equal to max
while(intProductIndexSubCategory > intProductIndexMaxSubCategory){

intProductIndexSubCategory -= (intProductIndexMaxSubCategory + 1);

}

clearRequests();
displaysLoading();

}
//"addScreen#absoluteCategory#CategoryShift#absoluteSubcategory#SubCategoryShift"
else if(strFunction == "addScreen"){

debugMessage("Screen added ... Updating the screens");

lstDisplays += [(string) sender_num + "#" + llList2String(lstMessage,1) + "#" + llList2String(lstMessage,2) + "#" + llList2String(lstMessage,3) + "#" + llList2String(lstMessage,4) ];

clearRequests();
llMessageLinked(LINK_SET,0,"addBuffer" ,NULL_KEY); //set new Buffer Length
displaysLoading();


}
//"itemBought#BuyerUUID"
else if(strFunction == "itemBought"){

key keyBuyerUUID = (key)llList2String(lstMessage,1);
list lstItemBought = llParseString2List(strCurrentItem,["#"],[]);
string strItemBought = llList2String(lstItemBought,3);

debugMessage( llKey2Name(keyBuyerUUID) + " has bought your item.");
llWhisper(0,"Thank you " + llKey2Name(keyBuyerUUID) + " for purchasing the " + strItemBought +".");

//If this is networked, request server to distribute object
if(keyServer != NULL_KEY){

//Send another E-mail Request hopefully, its just a missed e-mail request
//Make Request for product card UUID
//If there are available scripts dedicated to offloading the delay in llEmail, do so.
//If not, make the e-mail request within this script taking into account the 20 second
//script delay set in by LL to prevent e-mail spam. <:P
if(blnEmailAgents == TRUE){

llMessageLinked(LINK_SET,0,"EmailRequest#" + (string) keyServer + "@lsl.secondlife.com#"+ crypt("Give Item") +"#" + crypt((string) keyBuyerUUID + "^" + strItemBought) ,NULL_KEY);

}
else{

llEmail((string) keyServer + "@lsl.secondlife.com",crypt("Give Item"),crypt((string) keyBuyerUUID + "^" + strItemBought));

}


}
//Otherwise, give the person the object in its own inventory
else{

llGiveInventory(keyBuyerUUID, strItemBought);

}

}
//"infoRequested#requesterUUID"
else if(strFunction == "infoRequested"){

intInfoCount++;
key keyRequesterUUID = (key)llList2String(lstMessage,1);
debugMessage( llKey2Name(keyRequesterUUID) + " wants to know more about your business.");
// llGiveInventory(keyRequesterUUID,strInfoCardName);
// intInfoCount++;
//
// }









//If this is networked, request server to distribute notecard
if(keyServer != NULL_KEY){

//Send another E-mail Request hopefully, its just a missed e-mail request
//Make Request for product card UUID
//If there are available scripts dedicated to offloading the delay in llEmail, do so.
//If not, make the e-mail request within this script taking into account the 20 second
//script delay set in by LL to prevent e-mail spam. <:P
if(blnEmailAgents == TRUE){

llMessageLinked(LINK_SET,0,"EmailRequest#" + (string) keyServer + "@lsl.secondlife.com#"+ crypt("Give Note") +"#" + crypt((string) keyRequesterUUID + "^" + strInfoCardName) ,NULL_KEY);

}
else{

llEmail((string) keyServer + "@lsl.secondlife.com",crypt("Give Note"),crypt((string) keyRequesterUUID + "^" + strInfoCardName));

}


}
//Otherwise, give the person the object in its own inventory
else{

llGiveInventory(keyRequesterUUID, strInfoCardName);

}

}









//dbResults#QueryID#(product data)
else if(strFunction == "dbResults"){

processQuery(str);//check if response was needed

}
else{

//Ignore the message

}

}
//SEE NEXT REPLY FOR PART 2
Fleur Dollinger
Registered User
Join date: 31 Mar 2007
Posts: 8
FOSSLVendorMainClient Pt2
04-30-2008 18:40
CODE

//PART 2
//used to read vendor settings, calculate index lengths, and retrieve product data
dataserver(key query_id, string data) {

//Whenever vendor is reading the settings notecard.
//This usually happens once during script initialization
//Data should be an assignment per line
//Record format should be "variable=value"
if (query_id == keySettingsId) {
if (data != EOF) { // not at the end of the notecard

//split the record into variable names and values
list lstSplit = llParseString2List(data,["="],[]);
string strVariable = llToUpper(llStringTrim(llList2String(lstSplit,0),STRING_TRIM));
string strValue = llStringTrim(llList2String(lstSplit,1),STRING_TRIM);

if(strVariable == "LOADING TEXTURE"){

keyTexLoading = (key) strValue;

}
if(strVariable == "EMAIL"){

llMessageLinked(LINK_ROOT,950,strValue,"");

}
if(strVariable == "SEPARATE INFO"){
intSeparateInfo = (integer) strValue;
}
if(strVariable == "INFO SUFFIX"){
strINFO = strValue;
debugMessage("INFO CARD SUFFIX:" + strINFO);
}

if(strVariable == "OFFLINE TEXTURE"){

keyTexOffline = (key) strValue;

}
if(strVariable == "NOTHING TEXTURE"){

keyTexNothing = (key) strValue;

}
if(strVariable == "SERVER"){

keyServer = (key) strValue;

}
if(strVariable == "PRODUCT CARD"){

strProductsCardName = strValue;

}
if(strVariable == "INFO CARD"){

strInfoCardName = strValue;

}
if(strVariable == "COMM CHANNEL"){

intCommChannel = (integer) strValue;

}
if(strVariable == "REFRESH"){

fltInventoryRefresh = (float) strValue;

}
if(strVariable == "TIMEOUT"){

intServerTimeout = (integer) strValue;

}
if(strVariable == "BUFFER"){

intBuffer = (integer) strValue;

}
if(strVariable == "EMAIL AGENTS"){

blnEmailAgents = (integer) strValue;

}
if(strVariable == "KEY"){

strEncryptionKey = strValue;

}

keySettingsId = llGetNotecardLine("FOSSL_CLIENT_SETTINGS", ++intSettingLines); // request next line
}
//Settings notecard has been read, proceed with obtaining products card.
else{

llOwnerSay("FOSSLVendorClient: Vendor Settings loaded.");
llListen(intCommChannel,"",llGetOwner(),"count"); //activate listen event whenever I say count on the listening channel
llListen(intCommChannel,"",llGetOwner(),"debug"); //activate listen event whenever I say debug on the listening channel
llListen(intCommChannel,"",llGetOwner(),"reset"); //activate listen event whenever I say reset on the listening channel
llListen(intCommChannel,"",llGetOwner(),"online"); //activate listen event whenever I say online on the listening channel
llListen(intCommChannel,"",llGetOwner(),"offline"); //activate listen event whenever I say offline on the listening channel
llListen(intCommChannel,"",llGetOwner(),"memory"); //activate listen event whenever I say memory on the listening channel

//Is this vendor networked?
if(keyServer != NULL_KEY){

llOwnerSay("FOSSLVendorClient: Vendor is networked. Contacting Server for Product List.");

//If there are available scripts dedicated to offloading the delay in llEmail, do so.
//If not, make the e-mail request within this script taking into account the 20 second
//script delay set in by LL to prevent e-mail spam. <:P
if(blnEmailAgents == TRUE){

llMessageLinked(LINK_SET,0,"EmailRequest#" + (string) keyServer + "@lsl.secondlife.com#"+ crypt("Requesting Product List") + "#" + crypt("NULL"),NULL_KEY);

}
else{

llEmail((string) keyServer + "@lsl.secondlife.com",crypt("Requesting Product List"),crypt("NULL"));

}
blnWaitingForServer = TRUE;
intEmailRequestStarted = llGetUnixTime();
llSetTimerEvent(1.0);

}
//Otherwise, if it is not networked, load the product list uuid locally
else{

llOwnerSay("FOSSLVendorClient: Vendor is not networked. Obtaining Product List locally.");
keyProductsUUID = llGetInventoryKey(strProductsCardName);
llOwnerSay("FOSSLVendorClient: Determining Maximums for Categories and Subcategories. This may take a while...");
//start obtaining the # of categories and subcategories
keyProductCategories = llGetNotecardLine(keyProductsUUID,intProductLines);

}

//Update database buffer to the length specified by notecard
llMessageLinked(LINK_SET, 0, "setBuffer#" + (string) intBuffer, NULL_KEY);

}
}
//Whenever vendor is reading the products notecard to determine size of its categories and subcategories.
else if(query_id == keyProductCategories){

//If the products notecard needs to be read further...
if(data != EOF){

list lstProductParsedLine = llParseString2List(data,["#"],[]); //Parse the line into a list

//Determine if current Category # is the current Maximum
if(intProductIndexMaxCategory < (integer) llList2String(lstProductParsedLine,0)){

intProductIndexMaxCategory = (integer) llList2String(lstProductParsedLine,0);

}

//Determine if current SubCategory # is the current Maximum
if(intProductIndexMaxSubCategory < (integer) llList2String(lstProductParsedLine,1)){

intProductIndexMaxSubCategory = (integer) llList2String(lstProductParsedLine,1);

}

//Read the next line of the products notecard
keyProductCategories = llGetNotecardLine(keyProductsUUID,++intProductLines);

}
//After reading the notecard, vendor is ready to operate
else{

llOwnerSay("FOSSLVendorClient: Categories and SubCategories determined. Vendor is now able to load products.");

intProductLines = 0; //clear this for later updates
clearRequests(); //clear any requests for products if any
llMessageLinked(LINK_SET,0,"setBuffer#" + (string) intBuffer ,NULL_KEY);//clear the current list of products in buffer, if any
llMessageLinked(LINK_SET, 0, "setDB#" + (string) keyProductsUUID, NULL_KEY); //Tell Database where it gets its data
goOnline();

//Request displays to register if there are none already
if(lstDisplays == []){

llMessageLinked(LINK_SET,0,"getDisplays",NULL_KEY); //request all screens to register

}

//Only refresh the inventory if it is networked
if(keyServer != NULL_KEY){

llSetTimerEvent(fltInventoryRefresh); //Set timer to request product notecard uuid at a set interval

}

}

}

}

timer(){

//Depending on if the vendor is waiting from a response from the server,
//either wait for an e-mail from the server, or make a request for an updated product list
if(blnWaitingForServer == TRUE){

if(llGetUnixTime() - intEmailRequestStarted < intServerTimeout){

llGetNextEmail((string) keyServer + "@lsl.secondlife.com",crypt("Update Product List"));
//llGetNextEmail("","");
}
else{

//take vendor offline if not done already
goOffline();

//Send another E-mail Request hopefully, its just a missed e-mail request
//Make Request for product card UUID
//If there are available scripts dedicated to offloading the delay in llEmail, do so.
//If not, make the e-mail request within this script taking into account the 20 second
//script delay set in by LL to prevent e-mail spam. <:P
if(blnEmailAgents == TRUE){

llMessageLinked(LINK_SET,0,"EmailRequest#" + (string) keyServer + "@lsl.secondlife.com#"+ crypt("Requesting Product List") + "#" + crypt("NULL"),NULL_KEY);

}
else{

llEmail((string) keyServer + "@lsl.secondlife.com",crypt("Requesting Product List"),crypt("NULL"));

}

intEmailRequestStarted = llGetUnixTime(); //After this, timer loop will look for a server response

}

}
else{

//Make Request for product card UUID
//If there are available scripts dedicated to offloading the delay in llEmail, do so.
//If not, make the e-mail request within this script taking into account the 20 second
//script delay set in by LL to prevent e-mail spam. <:P
if(blnEmailAgents == TRUE){

llMessageLinked(LINK_SET,0,"EmailRequest#" + (string) keyServer + "@lsl.secondlife.com#"+ crypt("Requesting Product List") + "#" + crypt("NULL"),NULL_KEY);

}
else{

llEmail((string) keyServer + "@lsl.secondlife.com",crypt("Requesting Product List"),crypt("NULL"));

}

blnWaitingForServer = TRUE;
intEmailRequestStarted = llGetUnixTime(); //After this, timer loop will look for a server response
llSetTimerEvent(1.0);

}

}

email(string time, string address, string subject, string body, integer remaining){

debugMessage("Recieved E-mail from : " + address);

if (address == (string) keyServer + "@lsl.secondlife.com")
{
//don't wait for more e-mail
blnWaitingForServer = FALSE;

//make this the last time products were updated
intLastProductUpdate = llGetUnixTime();

//Remove e-mail header to generate the new uuid
key keyNewUUID = (key) crypt(llDeleteSubString(body, 0, llSubStringIndex(body, "\n\n") + 1));

//New product notecard uuid is in the message
keyProductsUUID = keyNewUUID;

//start obtaining the # of categories and subcategories
keyProductCategories = llGetNotecardLine(keyProductsUUID,intProductLines);

//polling for further e-mails using the timer event is no longer neccesary
llSetTimerEvent(0.0);

}



//}


if(remaining > 0 ){

//load the next e-mail in que to try and clear the e-mail buffer
llGetNextEmail((string) keyServer + "@lsl.secondlife.com",crypt("Update Product List"));
// llGetNextEmail("","");
// llGetNextEmail("","");

}


}

listen( integer channel, string name, key id, string message ){

if(id == llGetOwner()){

//Count the number of times users requested information
if(message == "count"){
llOwnerSay("FOSSLVendorClient: Notecard info request at " + (string)intInfoCount + ".");
}

//Enable or disable verbose output
else if(message == "debug"){

blnDebug = !blnDebug;
llOwnerSay("FOSSLVendorClient: Debug Mode set to: " + (string) blnDebug);
llMessageLinked(LINK_SET,0,"debugMode#" + (string) blnDebug,NULL_KEY);


}

//Make the vendor go online
else if(message == "online"){

goOnline();
debugMessage("Vendor attempting to go online");

}

//Make the vendor go offline
else if(message == "offline"){

goOffline();
debugMessage("Vendor attempting to go offline");

}

//Reset the scripts
else if(message == "reset"){

llResetScript();

}
//Get the current free memory in script
else if(message == "memory"){

llOwnerSay("FOSSLVendorClient: Memory = " + (string) llGetFreeMemory() +"/16000 Bytes");
llMessageLinked(LINK_SET,0,"getMemory",NULL_KEY); //If other scripts understand this, have them output free memory also

}
else{
llOwnerSay("FOSSLVendorClient: I dunno what youre talking about.");
}
}

}

}
Kinji Lockjaw
Registered User
Join date: 2 Aug 2008
Posts: 1
Fossly vending for magazines
08-03-2009 13:48
I bought the Fossl vending system but I don't want to sell items just use it to provide copyable, transfer vendors for my magazine. How do i go about doing that?
Rolig Loon
Not as dumb as I look
Join date: 22 Mar 2007
Posts: 2,482
08-03-2009 14:27
If you're not going to be handling any money, this script may be overkill. I think all your vendor units need is a way to update their contents every time you have a new issue to distribute, and then a way to hand a free copy of the issue to a customer. Take a look at for another, easier way to do that first task. The second task, handing out free copies, is just a matter of putting llGiveInventory(llDetectedKey(0), llGetInventoryName(INVENTORY_OBJECT,0)) in a touch_start event. You can build in bells and whistles to gather circulation statistics and such, but if all you need are these relatively simple tasks, why go with all that extra freight?
_____________________
It's hard to tell gender from names around here but if you care, Rolig = she. And I exist only in SL, so don't ask.... ;)

Look for my work in XStreetSL at