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.
configuration
from the configuration section.######################### 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 #################################
/////////////////////////////////////////////////////////////////////////// // 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(); } }