/////////////////////////////////////////////////////////////////////////// // 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 ; 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(); } }