About

This is an ample donation jar that is available on the Second Life marketplace and designed to be used as soon as it is unpacked. It features a configuration notecard which will easily let you adjust the settings of the donation jar.

Features

  • Configurable goal.
  • Configurable color of the overhead text.
  • Settable buttons for donations.
  • Auto-reset after the goal has been reached.
  • Shouts thanks on local chat.
  • Displays the last tipper and the tip amount.
  • Progress bar showing the percent to the settable goal.
  • Displays how much was donated in total.

Marketplace

Setup

This donation jar can be configured by editing the supplied configuration notecard to be found inside the main donation jar primitive. If you right-click and Edit... the donation jar, you will find a notecard named configuration inside the donation jar and if you edit that notecard, you will see the following lines:

#### START CONFIGURATION ####

# this is the goal you want to attain in L$
goal = 500

# this is the color of the overhead text
color = 0.5, 1, 1

# this is a list of pay buttons displayed 
# when an avatar click the donation jar
buttons = 10, 100, 500, 1000

##### END CONFIGURATON ######

Anything preceded by #, either a line or in-line is considered a comment and left out. The settings allow you to set a goal for your donation, the color of the overhead text and the tip button values. Whenever you change the notecard, the script will reset and the new values used in the configuration.

Technical Notes

We use the key-value data project in order to store variables over sessions. That is, in case the simulator restarts or the donation jar is de-rezzed, then the temporary data that is stored in-memory will be saved to the primitive's description.

Another feature is the reading of the configuration notecard, where comments starting with #, for both starting lines and in-line are ignored. This is done with an extension to the generic notecard reader that trims out the #-prefixed comments.

Code

ample_donation_jar.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2014 - 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) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasProgress(integer percent, integer length, list symbols) {
    percent /= (integer)((float)100.0/(length));
    string p = llList2String(symbols,0);
    integer itra = 0;
    do {
        if(itra>percent-1) p += llList2String(symbols,2);
        else p += llList2String(symbols,1);
    } while(++itra<length);
    return p + llList2String(symbols,3);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueSet(string k, string v, string data) {
    if(llStringLength(k) == 0) return "";
    if(llStringLength(v) == 0) return "";
    if(llStringLength(data) == 0) return k + "=" + v;
    integer i = llListFindList(
        llList2ListStrided(
            llParseString2List(data, ["&", "="], []), 
            0, -1, 2
        ), 
    [ k ]);
    if(i != -1) return llDumpList2String(
        llListReplaceList(
            llParseString2List(data, ["&"], []), 
            [ k + "=" + v ], 
        i, i), 
    "&");
    return data + "&" + k + "=" + v;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list data) {
    list k = llList2ListStrided(data, 0, -1, 2);
    list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
    data = [];
    do {
        data += llList2String(k, 0) + "=" + llList2String(v, 0);
        k = llDeleteSubList(k, 0, 0);
        v = llDeleteSubList(v, 0, 0);
    } while(llGetListLength(k) != 0);
    return llDumpList2String(data, "&");
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
    if(llStringLength(data) == 0) return "";
    if(llStringLength(k) == 0) return "";
    list a = llParseString2List(data, ["&", "="], []);
    integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
    if(i != -1) return llList2String(a, 2*i+1);
    return "";
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueDelete(string k, string data) {
    if(llStringLength(data) == 0) return "";
    if(llStringLength(k) == 0) return "";
    integer i = llListFindList(
        llList2ListStrided(
            llParseString2List(data, ["&", "="], []), 
            0, -1, 2
        ), 
    [ k ]);
    if(i != -1) return llDumpList2String(
        llDeleteSubList(
            llParseString2List(data, ["&"], []),
        i, i), 
    "&");
    return data;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
list wasCompoundToList(integer T, list compound) {
    if(llGetListLength(compound) == 0) return [];
    if(T == TYPE_FLOAT) return llList2Float(compound, 0) + 
        wasCompoundToList(T, llDeleteSubList(compound, 0, 0));
    if(T == TYPE_INTEGER) return llList2Integer(compound, 0) + 
        wasCompoundToList(T, llDeleteSubList(compound, 0, 0));
    if(T == TYPE_STRING) return llList2String(compound, 0) + 
        wasCompoundToList(T, llDeleteSubList(compound, 0, 0));
    if(T == TYPE_KEY) return llList2Key(compound, 0) + 
        wasCompoundToList(T, llDeleteSubList(compound, 0, 0));
    if(T == TYPE_VECTOR) return llList2Vector(compound, 0) + 
        wasCompoundToList(T, llDeleteSubList(compound, 0, 0));
    if(T == TYPE_ROTATION) return llList2Rot(compound, 0) + 
        wasCompoundToList(T, llDeleteSubList(compound, 0, 0));
    return wasCompoundToList(T, llDeleteSubList(compound, 0, 0));
}
 
integer line = 0;
list settings = [];
 
default {
    state_entry() {
        if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
            llSay(DEBUG_CHANNEL, "Sorry, could not find an inventory notecard.");
            return;
        }
        llGetNotecardLine("configuration", line);
    }
    dataserver(key id, string data) {
        if(data == EOF) state update;
        if(data == "") jump continue;
        integer i = llSubStringIndex(data, "#");
        if(i != -1) data = llDeleteSubString(data, i, -1);
        list o = llParseString2List(data, ["="], []);
        string k = llStringTrim(llList2String(o, 0), STRING_TRIM);
        string v = llStringTrim(llList2String(o, 1), STRING_TRIM);
        if(k == "" || v == "") jump continue;
        settings += k;
        settings += v;
@continue;
        llGetNotecardLine("configuration", ++line);
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        llResetScript();
    }
}
 
state donations {
    state_entry() {
        // Set the pay buttons and click action.
        llSetPayPrice(100, 
            wasCompoundToList(TYPE_INTEGER, 
                llParseString2List(
                    wasKeyValueGet("buttons", 
                        wasKeyValueEncode(settings)
                    ), 
                [",", " "], [])
            )
        );
        llSetClickAction(CLICK_ACTION_PAY);
    }
    money(key id, integer amount) {
        llSetObjectDesc(
            wasKeyValueSet(
                "lastname", 
                llList2String(llParseString2List(llKey2Name(id), [" "], [""]), 0), 
                llGetObjectDesc()
            )
        );
        llSetObjectDesc(
            wasKeyValueSet(
                "lastamount", 
                (string)amount, 
                llGetObjectDesc()
            )
        );
        llShout(0, "Thank you, " + llKey2Name(id) + " for your generous donation!");
        llSetObjectDesc(
            wasKeyValueSet("donated", 
                (string)((integer)wasKeyValueGet(
                    "donated", 
                    llGetObjectDesc()
                )+amount),
                llGetObjectDesc()
            )
        );
        state update;
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        llResetScript();
    }
}
 
state update {
    state_entry() {
        // Update the text with the current progress.
        integer percent = 100*(integer)wasKeyValueGet(
            "donated", llGetObjectDesc()
        )/(integer)wasKeyValueGet("goal", wasKeyValueEncode(settings));
        llSetText(
            "Donation Jar\nProgress: " + 
            wasProgress(percent, 5, ["[", "█", "░", "]"]) + 
            " %\n" + 
            (string)percent + "% of L$" + wasKeyValueGet("goal", wasKeyValueEncode(settings)), 
        (vector)("<" + wasKeyValueGet("color", wasKeyValueEncode(settings)) + ">"), 1);
        // If the goal hasn't been reached, jump to the doner section.
        if(percent < 100) jump doner;
        // If the goal has been attained or overstepped,
        // let the owner know, schedule a reset and return.
        llInstantMessage(llGetOwner(), "Donation goal attained at " + llGetRegionName() + "!");
        llSetObjectDesc(
            wasKeyValueSet("donated", 
                (string)0,
                llGetObjectDesc()
            )
        );
        llSetObjectDesc(
            wasKeyValueDelete(
                "lastname", 
                llGetObjectDesc()
            )
        );
        llSetObjectDesc(
            wasKeyValueDelete(
                "lastamount", 
                llGetObjectDesc()
            )
        );
        llResetScript();
@doner;
        // If somebody tipped and this is not a script restart,
        // then get the display text and add a thank you message 
        // for the last tipper. We grab just the first name of 
        // the tipper to make the donation jar more personal.
        string lastname = wasKeyValueGet("lastname", llGetObjectDesc());
        integer lastamount = (integer)wasKeyValueGet("lastamount", llGetObjectDesc());
        if(lastname == "") jump exit;
        if(lastamount == 0) jump exit;
        llSetText(llList2String(
            llGetPrimitiveParams(
                [ PRIM_TEXT ]
            ), 0) + 
            "\nLast donation: " + 
            (string)lastamount + 
            "L$" + 
            "\nThank you, " +  
            lastname + 
            "!", 
        <.5,1,1>, 1);
        // Go back to accepting donations.
@exit;
        state donations;
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        llResetScript();
    }
}

secondlife/donation_jar/ample_donation_jar.txt · Last modified: 2017/02/22 18:21 (external edit)

Access website using Tor


For the copyright, license, warranty and privacy terms for the usage of this website please see the license and privacy pages.