Table of Contents

About

This variation is a tipjar that will follow the owner through the simulator and provide quick-pay buttons for agents to donate money. The tipjar is configurable via a notecard called "configuration" that is placed in the same primitive as this script.

Features

  • Following personal tipjar that follows the owner on a simulator.
  • The tip jar stops moving and sends a message to the owner when the owner leaves in order to prevent littering.
  • The tip jar will poof yellow gold powder once a tipper pays money into the jar.
  • There are a number of configurable parameters via the "configuration" notecard inside the jar:
    • Settable dampening parameter to make the following tip jar move faster or slower around the owner.
    • The maximum range that the tip jar is allowed to wander from the owner is configurable.
    • The scanning range for the owner is configurable.
    • The tip buttons for paying into the tip jar are configurable.
    • The tip jar sends a private message to the tipper which is configurable.
    • Customizable overhead text color.
    • Customizable overhead text tile, eg: Joe's Awesome Tipjar!
  • The tip jar always displays in the overhead text the following data in order:
    • The tip jar title.
    • The last tip and the name of the last tipper.
    • The highest tip and the name of the highest tipper.
    • The total amount of tips given so far.

Setup

  • Create a new primitive and add a notecard called configuration from the configuration section.
  • Create a new script inside the same primitive and copy the contents from the code section.
  • Optionally, follow the instructions on the wanderer page to properly configure the wandering of the primitive.

Configuration

######################### START CONFIGURATION ################################

# This is a configuration notecard based on the key-value data Wizardry and Steamworks reader. 
# Everything following the "#" character is ignored along with blank lines and values can be set for 
# various parameters in a simple and understandable format with the following syntax: KEY = "VALUE" 
# Every time you change this configuration notecard, the script will reset itself and the new 
# configuration will be read from this notecard.

# This is the dampening time in seconds that the tip jar will use to move to its next destination. 
# Intuitively the larger the number the slower the movement and the smaller the number the faster the 
# tip jar will hoover around its owner. This setting must be an integer above 0.
dampening = "20"

# The maximum distance in meters from the owner that the tip jar will hover around. This does not 
# mean that the tip jar will not get close to the owner, but it rather represents the maximum distance 
# that the tip jar will be allowed to stray away from its owner.
radius = "10"

# This is the distance in meters to scan for the owner. If the owner is not found within this range, then 
# the tip jar attempts to find the owner on the entire simulator and then jumps to them. Intuitively, this 
# value should be larger than the "radius" range setting or the tip jar will keep jumping erratically.
sensor = "10"

# A list of values that will be presented whenever someone clicks your tip jar. There can only be four of 
# these buttons and they have to be positive and above zero.
buttons = "25, 50, 100, 200"

# A message that will be sent automatically to the tipper once they tip the owner.
thanks = "Thank you for the awesome tip!"

# A color vector for the color of the overhead text. This is in LSL notation and colors are represented in 
# a [0, 1] interval notation with 0 being the RGB equivalent of 0 and 1 being the RGB equivalent of 255.
# Here is a quick cheat sheet:
# <1, 0, 0> - red
# <0, 1, 0> - green
# <0, 0, 1> - blue
# <1, 1, 0> - yellow
# <1, 0, 1> - magenta
# <0, 1, 1> - cyan
color = "<1,1,0>"

# This is the title text that will appear above the statistics.
title = "-:[ Nanny's Tip Jar ]:-"

######################### END CONFIGURATION #################################

Code

following_personal_tipjar
///////////////////////////////////////////////////////////////////////////
//  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    //
///////////////////////////////////////////////////////////////////////////
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));
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
vector wasCirclePoint(float radius) {
    float x = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
    float y = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
    if(llPow(x,2) + llPow(y,2) <= llPow(radius,2))
        return <x, y, 0>;
    return wasCirclePoint(radius);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasCreateSLURL(string region, vector position) {
    list l = llParseString2List((string)position, ["<", ",", ">"], []);
    return "secondlife://" + llEscapeURL(region) + "/" + 
        (string)llList2Integer(l, 0) + "/" + 
        (string)llList2Integer(l, 1) + "/" + 
        (string)llList2Integer(l, 2);
}
 
moveTo(vector position) {
    llTarget(position, .8);
    llLookAt(position, .5, 1);
    llMoveToTarget(
        position, 
        llList2Float(
            tuples, 
            llListFindList(
                tuples, 
                [
                    "dampening"
                ]
            )+1
        )
    );    
}
 
// for notecard reading
integer line = 0;
 
// key-value data will be read into this list
list tuples = [];
 
// stores the current position of the object
vector mePosition = ZERO_VECTOR;
 
// storage for donations
list stash = [];
list users = [];
 
default {
    state_entry() {
        // set up initial tips
        users += llKey2Name(
            llGetOwner()
        );
        stash += 0;
        // stop all physics
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetStatus(STATUS_BLOCK_GRAB_OBJECT, FALSE);
        llVolumeDetect(FALSE);
        // read the configuration notecard
        state configure;
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            llResetScript();
        }
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state configure {
    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) { // invariant, length(tuples) % 2 == 0
            state wander;
        }
        if(data == "") jump continue;
        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 == "") jump continue;
        tuples += k;
        tuples += v;
@continue;
        llGetNotecardLine("configuration", ++line);
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            llResetScript();
        }
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state wander {
    state_entry() {
        // set overhead text.
        llSetText(
            llList2String(
                tuples, 
                llListFindList(
                    tuples, 
                    [
                        "title"
                    ]
                )+1
            ) +
            "\nLast tip: L$" + 
                (string)
                    llList2Integer(
                        stash, 
                    -1) + " by " + 
                llList2String(
                    users, 
                    -1
                ) +
            "\nHighest tip: L$" + 
                (string)(
                    (integer)
                        llListStatistics(
                            LIST_STAT_MAX, 
                            stash
                        )
                    ) + " by " + 
                llList2String(
                    users, 
                    llListFindList(
                        stash, 
                        [
                            (integer)llListStatistics(
                                LIST_STAT_MAX, 
                                stash
                            )
                        ]
                    )
            ) + 
            "\nTotal tips: L$" + 
                (string)
                    (
                        (integer)
                            llListStatistics(
                                LIST_STAT_SUM, 
                                stash
                            )
                    ), 
            (vector)
                llList2String(
                    tuples, 
                    llListFindList(
                        tuples, 
                        [
                            "color"
                        ]
                    )
                +1), 
            1
        );
        // set pay buttons
        list price = wasCompoundToList(
            TYPE_INTEGER, 
            llParseString2List(
                llList2String(
                    tuples, 
                    llListFindList(
                        tuples, 
                        [
                            "buttons"
                        ]
                    )+1
                ), 
                [",", " "], 
                []
            )
        );
        // set click action to pay
        llSetClickAction(CLICK_ACTION_PAY);
        llSetPayPrice(llList2Integer(price, 0), price);
        // scan for owner
        llSensorRepeat(
            "", 
            llGetOwner(), 
            AGENT, 
            llList2Float(
                tuples, 
                llListFindList(
                    tuples, 
                    [
                        "sensor"
                    ]
                )+1
            ), 
            TWO_PI, 
            1
        );
    }
    sensor(integer num) {
        mePosition = llDetectedPos(0);
        llSetStatus(STATUS_PHYSICS, TRUE);
        llSetStatus(STATUS_BLOCK_GRAB_OBJECT, TRUE);
        llVolumeDetect(TRUE);
        moveTo(
            mePosition + 
            wasCirclePoint(
                llList2Float(
                    tuples, 
                    llListFindList(
                        tuples, 
                        [
                            "radius"
                        ]
                    )+1
                )
            )
        );
    }
    no_sensor() {
        // stop physics
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetStatus(STATUS_BLOCK_GRAB_OBJECT, FALSE);
        llVolumeDetect(FALSE);
        // if the owner is not on the current simulator, send them a message
        list o = llGetObjectDetails(llGetOwner(), [OBJECT_POS]);
        if(o == []) {
            llInstantMessage(
                llGetOwner(), 
                "Hey! I'm your tip jar, you forgot to pick me up. I'm at: " + 
                wasCreateSLURL(
                    llGetRegionName(), 
                    llGetPos()
                )
            );
            return;
        }
        mePosition = (vector)llList2String(o,0);
        // reposition in a 1m range radius from owner
        llSetRegionPos(mePosition + wasCirclePoint(1));
    }
    at_target(integer tnum, vector targetpos, vector ourpos) {
        if(
            llVecDist(
                ourpos, 
                targetpos
            ) > llList2Float(
                tuples, 
                llListFindList(
                    tuples, 
                    [
                        "radius"
                    ]
                )+1
            )
        ) return;
        moveTo(
            mePosition + 
            wasCirclePoint(
                llList2Float(
                    tuples, 
                    llListFindList(
                        tuples, 
                        [
                            "radius"
                        ]
                    )+1
                )
            )
        );
    }
    money(key id, integer amount) {
        // thank the tipper
        llInstantMessage(
            id, 
            llList2String(
                tuples, 
                llListFindList(
                    tuples, 
                    [
                        "thanks"
                    ]
                )+1
            )
        );
        // add the user and the tip to memory
        users += llKey2Name(id);
        stash += amount;
        // set overhead text.
        llSetText(
            llList2String(
                tuples, 
                llListFindList(
                    tuples, 
                    [
                        "title"
                    ]
                )+1
            ) +
            "\nLast tip: L$" + 
                (string)
                    llList2Integer(
                        stash, 
                    -1) + " by " + 
                llList2String(
                    users, 
                    -1
                ) +
            "\nHighest tip: L$" + 
                (string)(
                    (integer)
                        llListStatistics(
                            LIST_STAT_MAX, 
                            stash
                        )
                    ) + " by " + 
                llList2String(
                    users, 
                    llListFindList(
                        stash, 
                        [
                            (integer)llListStatistics(
                                LIST_STAT_MAX, 
                                stash
                            )
                        ]
                    )
            ) + 
            "\nTotal tips: L$" + 
                (string)
                    (
                        (integer)
                            llListStatistics(
                                LIST_STAT_SUM, 
                                stash
                            )
                    ), 
            (vector)
                llList2String(
                    tuples, 
                    llListFindList(
                        tuples, 
                        [
                            "color"
                        ]
                    )
                +1), 
            1
        );
        // Trigger poof effect
        llParticleSystem(
            [
                PSYS_SRC_TEXTURE, "0cb260bf-1874-cb0e-b406-fb7065759b0c",
                PSYS_PART_START_SCALE, < .08, .08, FALSE >, PSYS_PART_END_SCALE, < .1, .1, FALSE >,
                PSYS_PART_START_COLOR, < 1, 1, 0 >, PSYS_PART_END_COLOR, < 1, 1, 0 >,
                PSYS_PART_START_ALPHA, 1, PSYS_PART_END_ALPHA, .1,
                PSYS_SRC_BURST_PART_COUNT, 1,
                PSYS_SRC_BURST_RATE, .05,
                PSYS_PART_MAX_AGE, 4,
                PSYS_SRC_MAX_AGE, 5,
                PSYS_SRC_PATTERN, 2,
                PSYS_SRC_BURST_SPEED_MIN, 0, PSYS_SRC_BURST_SPEED_MAX, 0,
                PSYS_SRC_BURST_RADIUS, 1.5,
                PSYS_SRC_TARGET_KEY, llGetKey(),
                PSYS_PART_FLAGS, 
                    PSYS_PART_INTERP_COLOR_MASK |
                    PSYS_PART_INTERP_SCALE_MASK |
                    PSYS_PART_EMISSIVE_MASK |
                    PSYS_PART_FOLLOW_VELOCITY_MASK |
                    PSYS_PART_TARGET_POS_MASK
            ]
        );
        llSetTimerEvent(5);
    }
    timer() {
        llSetTimerEvent(0);
        llParticleSystem([]);
    }
    changed(integer change) {
        if(change & CHANGED_INVENTORY) {
            llResetScript();
        }
    }
    on_rez(integer num) {
        llResetScript();
    }
}

secondlife/tipjar/following_personal_tipjar.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.