Table of Contents

About

This variation adds the capability for donation jars placed on the same simulator to synchronise the donation amounts and user-data.

Screenshot

Configuration

The script uses a notecard named configuration that must be placed in the same primitive as the script:

#### START CONFIGURATION ####

# the title to show in hoover-text above
# the progress bar and the statistics.
title = "Synchronized Donation Jar"

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

# this is an LSL color vector which will 
# be used for the overhead text
color = <0.5, 0.5, 1>

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

# the synchronization password must
# is the shared secret between all the
# donation jars that will synchronize.
password = "feelin'blu!"

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

Code

///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2021 - 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(data) == 0) return k + "=" + v;
    if(llStringLength(k) == 0) return "";
    if(llStringLength(v) == 0) return "";
    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: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
    if(llStringLength(data) == 0) return "";
    if(llStringLength(k) == 0) return "";
    list a = llParseStringKeepNulls(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 = [];
integer pipe = 0;
 
default {
    state_entry() {
        // generate synchronization channel
        pipe = (integer)("0x8" + llGetSubString(llGetOwner(), 0, 6));
        if(llGetInventoryType(llGetInventoryName(INVENTORY_NOTECARD, 0)) != INVENTORY_NOTECARD) {
            llSay(DEBUG_CHANNEL, "Sorry, could not find an inventory notecard.");
            return;
        }
        llGetNotecardLine(
            llGetInventoryName(
                INVENTORY_NOTECARD, 
                0
            ), 
            line
        );
    }
    dataserver(key id, string data) {
        if(data == EOF) {
            // invariant, length(tuples) % 2 == 0
            if(llGetListLength(settings) % 2 == 0) state update;
            llSay(DEBUG_CHANNEL, "Error in configuration notecard.");
            return;
        } 
        if(data == "") {
            llGetNotecardLine(
                llGetInventoryName(
                    INVENTORY_NOTECARD, 
                    0
                ), 
                ++line
            );
            return;
        }
        integer i = llSubStringIndex(data, "#");
        if(i != -1) data = llDeleteSubString(data, i, -1);
        list o = llParseString2List(data, ["="], []);
        // get rid of starting and ending quotes
        string k = llDumpList2String(
            llParseString2List(
                llStringTrim(
                    llList2String(
                        o, 
                        0
                    ), 
                STRING_TRIM), 
            ["\""], []
        ), "\"");
        string v = llDumpList2String(
            llParseString2List(
                llStringTrim(
                    llList2String(
                        o, 
                        1
                    ), 
                STRING_TRIM), 
            ["\""], []
        ), "\"");
        if(k == "" || v == "") {
            llGetNotecardLine(
                llGetInventoryName(
                    INVENTORY_NOTECARD, 
                    0
                ), 
                ++line
            );
            return;
        }
        settings += k;
        settings += v;
        llGetNotecardLine(
            llGetInventoryName(
                INVENTORY_NOTECARD, 
                0
            ), 
            ++line
        );
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        llResetScript();
    }
}
 
state donations {
    state_entry() {
        // Listen on the synchronization channels.
        llListen(pipe, "", "", "");
        // Set the pay buttons and click action.
        llSetPayPrice(100, 
            wasCompoundToList(TYPE_INTEGER, 
                llParseString2List(
                    wasKeyValueGet("buttons", 
                        wasKeyValueEncode(settings)
                    ), 
                [",", " "], [])
            )
        );
        llSetClickAction(CLICK_ACTION_PAY);
    }
    listen(integer channel, string name, key id, string message) {
        string password = wasKeyValueGet("password", message);
        // no password was sent bail - are you tickling my channels?
        if(password == "") return;
        // bail if the password does not match our settings
        if(password != wasKeyValueGet("password", wasKeyValueEncode(settings))) {
            return;
        }
        // overload message with the password filtered
        message = wasKeyValueDelete("password", message);
        // this should not happen at this point
        if(message == "") return;
        // and commit the rest
        llSetObjectDesc(message);
        // switch to update
        state update;
    }
    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()
            )
        );
        // Overload the description with the password from the
        // settings and then pass the message through the pipe.
        llRegionSay(
            pipe, 
            wasKeyValueSet(
                "password", 
                wasKeyValueGet(
                    "password", 
                    wasKeyValueEncode(settings)
                ), 
                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(
            wasKeyValueGet("title", 
                wasKeyValueEncode(settings)
            ) + "\nGoal: " + 
            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) {
            // 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 == "") {
                // Go back to accepting donations.
                state donations;
            }
            if(lastamount == 0) {
                // Go back to accepting donations.
                state donations;
            }
            llSetText(llList2String(
                llGetPrimitiveParams(
                    [ PRIM_TEXT ]
                ), 0) + 
                "\nLast donation: " +
                "L$" +  
                (string)lastamount + 
                " from " + lastname + 
                "!" + 
                "\nTotal donations: " +
                "L$" + 
                wasKeyValueGet(
                    "donated", llGetObjectDesc()
                ), 
            (vector)wasKeyValueGet("color", wasKeyValueEncode(settings)), 1);
            state donations;
        }
        // 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();
    }
    on_rez(integer num) {
        llResetScript();
    }
    changed(integer change) {
        llResetScript();
    }
}

secondlife/donation_jar/synchronized.txt · Last modified: 2022/11/24 07:46 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


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