User:Tutti Anatine/scripts

From Second Life Wiki
< User:Tutti Anatine
Revision as of 14:10, 12 April 2014 by Tutti Anatine (talk | contribs) (→‎Picture Slideshow scripts: Updated scripts from v1.0 to v1.1)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

<< Return to Tutti Anatine

These are open source scripts I have written. You are free to alter and distribute these scripts, but not sell them for more than L$0. If you alter the script, make sure to add it to the comments on top of the script. Do not remove existing comments. There is no need to credit me when using these scripts, but do credit when you use part of my code in your own script.

Please make sure the scripts are distributed with full permissions.

Back-up Box

A script that turns a newly rezzed prim into a Back-up Box, with unpacker function. Box name and inventory count will be displayed as floating text above the box itself.

Inexperienced-user-friendly options include:

  • Custom preset box texture (applied when resetting the script)
  • Custom floating text color
  • Custom floating text transparency
  • Optional box name prefix
    • This option is added to make reusing of the script with the same settings from inventory possible.

<lsl>// [*TA*] Back-up Box script // By Tutti Anatine key CREATOR = "95eaa123-5b83-437e-9fe9-90def840da39";

// This scripts creates a quick Back-up Box, in which you can store items to back them up in your inventory. // This solution can help keeping your inventory small and clutter-free.

// How to use: // Rez a new box prim (or an other shape of your preference) from the build menu. // Drag this script into the box's contents, the script should set up the box for you. // After this, put items into the contents of this box. // You can unpack the box by touching the box and selecting "Unpack" from the menu.

// You can give the box any name you wish, this will be the name your box will have in your inventory. // The unpack function will give you a folder, which is named after the Back-up Box's description field. // Should your description field be empty, the box will be called "Anonymous Back-up Box". // If you wish, you can also add a prefix to the name of the folder by adding it in the settings below. // Using the settings, you can define some default values which every box has to use and take the script into your inventory for easy reusing.

// This script is open source. Therefore, you are free to use this script, modify it and spread (only) with full permissions. // You are not allowed to charge more than L$0 for this script. If you have paid for this script, ask for a refund! // Please do not remove comment lines and do update them where neccesary if you have modified the script.

// -------------------- // settings // --------------------

// constants key BOX_TEXTURE = "b65eadd8-904f-f304-6e14-31e4e74d180d"; // original texture key: b65eadd8-904f-f304-6e14-31e4e74d180d made by Vmanhalk Wylie (vmanhalk.wylie) vector TEXT_COLOR = <1.0,1.0,1.0>; // text color // order: Red, Green, Blue // <1.0,1.0,1.0> = white, <0.0,0.0,0.0> = black float TEXT_ALPHA = 1.0; // floating text transparancy // 1.0 = visible, 0.0 = invisible string BOX_NAME_PREFIX = ""; // optional folder name prefix

// -------------------- // do not enter below this line unless you know what you're doing! // --------------------

//constants integer DIALOG_CHANNEL = -46346436; // channel used for dialog communication float TIME_OUT = 30.0; // number of seconds before timer activates

// variables key owner; // owner of the containing object string boxDescription; // description of the containing object integer numberOfContents; // number of contents inside containing object (this script not included) list inventoryList; // list containing the names of containing object's contents (this script not included) integer dialogHandler; // the chat channel handler for the dialog, will be removed by timer

init() { llListenRemove(dialogHandler);

owner = llGetOwner(); update(); }

// update inventoryList and floating text above containing object update() { boxDescription = llGetObjectDesc(); numberOfContents = llGetInventoryNumber(INVENTORY_ALL) - 1;

if (boxDescription == "") { boxDescription = "Anonymous Back-up Box"; }

// fill inventoryList with names of containing object's contents inventoryList = []; integer i; for (i = 0; i <= numberOfContents; i++) { string name = llGetInventoryName(INVENTORY_ALL,i); if (name != llGetScriptName()) { inventoryList += [name]; } }

// update floating text above containing object string body = (string) llKey2Name(owner)+"'s Back-up Box\n\""+boxDescription+"\"\n "+(string) numberOfContents+" item(s) inside"; llSetText(body, TEXT_COLOR, TEXT_ALPHA); }

default {

   state_entry() {
   	llSetTexture(BOX_TEXTURE, ALL_SIDES);
   	
       init();
   }
   
   touch_start(integer num_detected) {
   	if (llDetectedKey(0) == owner) {
   		dialogHandler = llListen(DIALOG_CHANNEL, "", owner, "");
   		llDialog(owner, "Please choose an option.\n\nPage 1/1", ["Unpack","Reset","Close"], DIALOG_CHANNEL);
   		llSetTimerEvent(TIME_OUT);
   	}
   }
   
   listen(integer channel, string name, key id, string message) {
   	if (channel == DIALOG_CHANNEL) {
   		llSetTimerEvent(0.0);
   		
   		message = llToLower(message);
   		if (message == "unpack") {
   			if (numberOfContents > 0) {
   				string folderName;
   				if (BOX_NAME_PREFIX != "") {
   					folderName = BOX_NAME_PREFIX+" "+boxDescription;
   				} else {
   					folderName = boxDescription;
   				}
   				llGiveInventoryList(owner, folderName, inventoryList);
   			} else {
   				llInstantMessage(owner, "This Back-up Box has no contents beside this script. If you wish to remove the script from the box, please do so via 'Contents' in the Edit window.");
   			}
   		} else if (message == "reset") {
   			llInstantMessage(owner, "This Back-up Box will now reset, this should have no effect on items inside the box.");
   			llResetScript();
   		}
   	}
   }
   
   changed(integer change) {
   	if (change & CHANGED_INVENTORY || change & CHANGED_ALLOWED_DROP) {
   		update();
   	} else if (change & CHANGED_OWNER || change & CHANGED_REGION_START) {
   		init();
   	}
   }
   
   timer() {
   	llListenRemove(dialogHandler);
   	llInstantMessage(owner, "Dialog Handler was removed. This Back-up Box will no longer respond to dialogs (menus) opened before this message appeared. You can touch the box again to reactivate the menu.");
   }
   
   on_rez(integer start_param) {
   	init();
   }

}</lsl>

Known issues

  • The unpacker script will not hand out no-copy items from inventory.

Picture Slideshow scripts

Find these scripts with example objects on the Marketplace.

Fading

<lsl> // [*TA*] Fading Picture Slideshow // Version: 1.1 // Last edit: 12 April 2014

// ---------------------------------------------------------------------------- // Script information // ----------------------------------------------------------------------------

string CREATOR = "tutti.anatine"; key CREATOR_ID = "95eaa123-5b83-437e-9fe9-90def840da39";

/* This is a single script which can drive a picture frame that fades between

* multiple pictures. The script expects the following setup:
* 1. A root prim containing this script;
* 2. A display prim called "display1";
* 3. A second display prim called "display2", placed directly behind the first
*    display prim.
* 
* Once the script is in place, it will take all the textures placed inside the
* root prim and display them on one of the display screens. The first display
* will be faded in and out to make it seem like the picture are blending during
* their transition.
*
* The display time, transition time and transition smoothness can be changed in
* the user settings below.
*
* It is not possible to display textures and photos which have no-copy rights
* to the owner of the script. The script will ignore these no-copy items.
* 
* The script can apply a base texture to the entire frame object. This feature
* can be enabled and disabled in the user settings below.
*
* The script's changelog can be found at the bottom of this script. */

// ---------------------------------------------------------------------------- // License // ----------------------------------------------------------------------------

/* This script is provided free of charge. If you have paid for it, ask for a

* refund. You are free to use the code for your own purposes. However, if you
* plan to redistribute this script, keep this header in place and intact. Also
* keep the script full-perm. */

// ---------------------------------------------------------------------------- // User settings // ----------------------------------------------------------------------------

/**

* Whether to apply a base texture to the picture frame. Possible values:
* TRUE/FALSE.
*/

integer STYLE_FRAME = TRUE;

/**

* The base texture used when STYLE_FRAME is set to TRUE.
* 
* This will usually be a texture's UUID key. When using a key, make sure the
* entire value is surrounded by double quotes, like: "key-key-key". You can
* also use TEXTURE_BLANK or TEXTURE_PLYWOOD to use a default blank or plywood
* texture. The latter values do not have to be surrounded by quotes.
*/

key FRAME_TEXTURE = TEXTURE_BLANK;

/**

* The number of seconds a picture should be displayed.
*/

float DISPLAY_TIME = 15.0;

/**

* The number of seconds it should take for the picture transition to complete.
*/

float FADE_TIME = 2.0;

/**

* The number of steps taken to fade a picture in or out. Increasing this
* number will make the picture transition seem smoother, but will also cause
* more lag.
*/

integer NUM_STEPS = 20;

/**

* The name of the (child) prim that acts as the first display. Defaults to
* "display1".
*/

string DISPLAY_1 = "display1";

/**

* The name of the (child) prim that acts as the second display. Defaults to
* "display2".
*/

string DISPLAY_2 = "display2";

// ---------------------------------------------------------------------------- // Do not edit below this line unless you know what you're doing! // ----------------------------------------------------------------------------

integer FACE = 0;

key owner; integer link_num; integer direction; integer display1; integer display2; float step_size; float sleep_time; list pictures; integer num_pics; integer next_pic;

// ----------------------------------------------------------------------------

/**

* Display an info message.
*
* @param string message The message to display.
*/

info(string message) { llOwnerSay("[INFO] " + message); //llSay(0, "[INFO] " + message); }

/**

* Display a debug message.
*
* Use only while developing and troubleshooting.
*
* @param string message The message to display.
*/

debug(string message) { // Comment out the lines below to stop producing error messages. //llOwnerSay("[DEBUG] " + message); //llSay(0, "[DEBUG] " + message); }

/**

* Set base variables and then run all setup functions.
*/

init() { info("Setting up the slideshow."); owner = llGetOwner(); link_num = llGetNumberOfPrims(); direction = 0;

style_frame(); determine_display_links(); calculate_step_values(); scan_pictures(); setup_displays(); info("Finished setting up the slideshow."); }

/**

* Apply the default texture to the entire object.
*/

style_frame() { debug("style_frame()"); // Make sure the owner has enabled this option. if (STYLE_FRAME == TRUE) { integer i = link_num; do { llSetLinkTexture(i, FRAME_TEXTURE, ALL_SIDES); } while (i--); } }

/**

* Set the link number for both display, based on their name.
*/

determine_display_links() { debug("determine_display_links()"); integer i = link_num; integer found = 0; do { if(llGetLinkName(i) == DISPLAY_1) { display1 = i; found++; debug("Found display 1."); } else if (llGetLinkName(i) == DISPLAY_2) { display2 = i; found++; debug("Found display 2."); } } while (i-- && found < 2); }

/**

* Determine the amount of alpha should be added or subtracted per step and the
* number of seconds that should be between the execution of each step.
*/

calculate_step_values() { debug("calculate_step_values()"); step_size = 100.0 / NUM_STEPS; sleep_time = FADE_TIME / NUM_STEPS; }

/**

* Search the object's inventory for textures and put each texture's UUID into
* a list. Also save the number of entries in the list in a separate variable.
*/

scan_pictures() { debug("scan_pictures()"); info("Scanning inventory for images."); pictures = []; num_pics = 0;

integer count_pics = llGetInventoryNumber(INVENTORY_TEXTURE); if (count_pics > 0) { integer i = 0; integer no_copy = FALSE; for (i; i < count_pics; i++) { string name = llGetInventoryName(INVENTORY_TEXTURE, i); if (llGetInventoryPermMask(name, MASK_OWNER) & PERM_COPY) { pictures += [llGetInventoryKey(name)]; } else { no_copy = TRUE; } } num_pics = llGetListLength(pictures);

if (no_copy == TRUE) { llInstantMessage(owner, "This frame contains no-copy images. Those cannot be displayed."); } } info("Finished scanning inventory for images."); }

/**

* Prepare the displays for their first run.
*/

setup_displays() { debug("setup_displays()"); llSetLinkAlpha(display1, 1.0, FACE); if(num_pics > 0) { llSetLinkTexture(display1, llList2Key(pictures, 0), FACE); if(num_pics > 1) { llSetLinkTexture(display2, llList2Key(pictures, 1), FACE); next_pic = 2; } else { llSetLinkTexture(display2, TEXTURE_BLANK, FACE); next_pic = 1; } } else { llSetLinkTexture(display1, TEXTURE_BLANK, FACE); llSetLinkTexture(display2, TEXTURE_BLANK, FACE); next_pic = 0; } }

/**

* Fade the front display in or out, by increasing or decreasing its
* transparency. Also prepare the next picture.
*/

fade() { debug("fade()"); llSetTimerEvent(0.0); float step; float current;

if (direction == 0) { // fade out current = 0.0; step = step_size; direction = 1; } else { // fade in current = 100.0; step = step_size * -1; direction = 0; }

   integer i = 0;
   for (i; i < NUM_STEPS; i++) {
   	current += step;
   	llSetLinkAlpha(display1, current / 100.0, 0);
   	llSleep(sleep_time);
   }
   prepare_next_pic();
   llSetTimerEvent(DISPLAY_TIME);

}

/**

* Determines which picture should be displayed next. Apply this picture to the
* currently hidden display.
*/

prepare_next_pic() { debug("prepare_next_pic()"); next_pic++; if (next_pic >= num_pics) { next_pic = 0; } debug("next_pic: " + (string) next_pic);

integer display; if (direction == 0) { display = display1; } else { display = display2; } llSetLinkTexture(display, llList2Key(pictures, next_pic), FACE); }

// ----------------------------------------------------------------------------

default { state_entry() { debug("state_entry()"); init(); if (num_pics > 1) { fade(); } }

touch_start(integer num_detected) { debug("touch_start(integer num_detected)"); if (num_pics > 1) { fade(); } }

changed(integer change) { debug("changed(integer change)"); if (change & CHANGED_INVENTORY || change & CHANGED_ALLOWED_DROP) { info("Inventory changed, reloading pictures."); llSetTimerEvent(0.0); scan_pictures();

if (num_pics < 2) { setup_displays(); } else { llSetTimerEvent(DISPLAY_TIME); } } else if (change & CHANGED_OWNER) { info("Owner changed, reloading slideshow."); init(); } }

timer() { debug("timer()"); fade(); } }

// ---------------------------------------------------------------------------- // Changelog // ----------------------------------------------------------------------------

/* v1.1

* 
* - Added info and debug message functions
* - Added info messages through the info function
* - Added debug messages through the debug function
* - Fixed calculation error in prepare_next_pic() which prevented a counter
*   reset on objects containing only two pictures. */

</lsl>

Simple

<lsl> // [*TA*] Simple Picture Slideshow // Version: 1.1 // Last edit: 12 April 2014

// ---------------------------------------------------------------------------- // Script information // ----------------------------------------------------------------------------

string CREATOR = "tutti.anatine"; key CREATOR_ID = "95eaa123-5b83-437e-9fe9-90def840da39";

/* This is a single script which can drive a picture frame that switches between

* multiple pictures. The script expects the following setup:
* 1. A root prim containing this script;
* 2. A display prim called "display1";
* 
* Once the script is in place, it will take all the textures placed inside the
* root prim and displays them on the display screen. After a set amount of
* seconds, the next image will be displayed.
*
* The display time can be changed in the user settings below.
*
* It is not possible to display textures and photos which have no-copy rights
* to the owner of the script. The script will ignore these no-copy items.
* 
* The script can apply a base texture to the entire frame object. This feature
* can be enabled and disabled in the user settings below.
*
* The script's changelog can be found at the bottom of this script. */

// ---------------------------------------------------------------------------- // License // ----------------------------------------------------------------------------

/* This script is provided free of charge. If you have paid for it, ask for a

* refund. You are free to use the code for your own purposes. However, if you
* plan to redistribute this script, keep this header in place and intact. Also
* keep the script full-perm. */

// ---------------------------------------------------------------------------- // User settings // ----------------------------------------------------------------------------

/**

* Whether to apply a base texture to the picture frame. Possible values:
* TRUE/FALSE.
*/

integer STYLE_FRAME = TRUE;

/**

* The base texture used when STYLE_FRAME is set to TRUE.
* 
* This will usually be a texture's UUID key. When using a key, make sure the
* entire value is surrounded by double quotes, like: "key-key-key". You can
* also use TEXTURE_BLANK or TEXTURE_PLYWOOD to use a default blank or plywood
* texture. The latter values do not have to be surrounded by quotes.
*/

key FRAME_TEXTURE = TEXTURE_BLANK;

/**

* The number of seconds a picture should be displayed.
*/

float DISPLAY_TIME = 15.0;

/**

* The name of the (child) prim that acts as the first display. Defaults to
* "display1".
*/

string DISPLAY_1 = "display1";

// ---------------------------------------------------------------------------- // Do not edit below this line unless you know what you're doing! // ----------------------------------------------------------------------------

integer FACE = 0;

key owner; integer link_num; integer display; float sleep_time; list pictures; integer num_pics; integer next_pic;

// ----------------------------------------------------------------------------

/**

* Display an info message.
*
* @param string message The message to display.
*/

info(string message) { llOwnerSay("[INFO] " + message); //llSay(0, "[INFO] " + message); }

/**

* Display a debug message.
*
* Use only while developing and troubleshooting.
*
* @param string message The message to display.
*/

debug(string message) { // Comment out the lines below to stop producing error messages. //llOwnerSay("[DEBUG] " + message); //llSay(0, "[DEBUG] " + message); }

/**

* Set base variables and then run all setup functions.
*/

init() { info("Setting up the slideshow."); owner = llGetOwner(); link_num = llGetNumberOfPrims();

style_frame(); determine_display_link(); scan_pictures(); setup_display(); info("Finished setting up the slideshow."); }

/**

* Apply the default texture to the entire object.
*/

style_frame() { debug("style_frame()"); // Make sure the owner has enabled this option. if (STYLE_FRAME == TRUE) { integer i = link_num; do { llSetLinkTexture(i, FRAME_TEXTURE, ALL_SIDES); } while (i--); } }

/**

* Set the link number for both display, based on their name.
*/

determine_display_link() { debug("determine_display_link()"); integer i = link_num; do { if(llGetLinkName(i) == DISPLAY_1) { display = i; } } while (i--); }

/**

* Search the object's inventory for textures and put each texture's UUID into
* a list. Also save the number of entries in the list in a separate variable.
*/

scan_pictures() { debug("scan_pictures()"); info("Scanning inventory for images."); pictures = []; num_pics = 0;

integer count_pics = llGetInventoryNumber(INVENTORY_TEXTURE); if (count_pics > 0) { integer i = 0; integer no_copy = FALSE; for (i; i < count_pics; i++) { string name = llGetInventoryName(INVENTORY_TEXTURE, i); if (llGetInventoryPermMask(name, MASK_OWNER) & PERM_COPY) { pictures += [llGetInventoryKey(name)]; } else { no_copy = TRUE; } } num_pics = llGetListLength(pictures);

if (no_copy == TRUE) { llInstantMessage(owner, "This frame contains no-copy images. Those cannot be displayed."); } } }

/**

* Prepare the display for its first run.
*/

setup_display() { debug("setup_display()"); llSetLinkAlpha(display, 1.0, FACE); if(num_pics > 0) { llSetLinkTexture(display, llList2Key(pictures, 0), FACE); next_pic = 1; } else { llSetLinkTexture(display, TEXTURE_BLANK, FACE); next_pic = 0; } }

/**

* Display the next picture.
*/

next() { debug("next()"); llSetTimerEvent(0.0);

next_pic++; if (next_pic >= num_pics) { next_pic = 0; } debug("next_pic: " + (string) next_pic);

llSetLinkTexture(display, llList2Key(pictures, next_pic), FACE); llSetTimerEvent(DISPLAY_TIME); }

// ----------------------------------------------------------------------------

default { state_entry() { debug("state_entry()"); init(); if (num_pics > 1) { next(); } }

touch_start(integer num_detected) { debug("touch_start(integer num_detected)"); if (num_pics > 1) { next(); } }

changed(integer change) { debug("changed(integer change)"); if (change & CHANGED_INVENTORY || change & CHANGED_ALLOWED_DROP) { info("Inventory changed, reloading pictures."); llSetTimerEvent(0.0); scan_pictures();

if (num_pics < 2) { setup_display(); } else { llSetTimerEvent(DISPLAY_TIME); } } else if (change & CHANGED_OWNER) { info("Owner changed, reloading slideshow."); init(); } }

timer() { debug("timer()"); next(); } }

// ---------------------------------------------------------------------------- // Changelog // ----------------------------------------------------------------------------

/* v1.1

* 
* - Added info and debug message functions
* - Added info messages through the info function
* - Added debug messages through the debug function
* - Fixed calculation error in next() which prevented a counter reset on
*   objects containing only two pictures. */

</lsl>

Floating text

<lsl> // [*TA*] Floating Text // Version: 1.0 // Last edit: 21 february 2013

// ---------------------------------------------------------------------------- // Script information // ----------------------------------------------------------------------------

string CREATOR = "tutti.anatine"; key CREATOR_ID = "95eaa123-5b83-437e-9fe9-90def840da39";

// ---------------------------------------------------------------------------- // License // ----------------------------------------------------------------------------

/* This script is provided free of charge. If you have paid for it, ask for a

* refund. You are free to use the code for your own purposes. However, if you
* plan to redistribute this script, keep this header in place and intact. Also
* keep the script full-perm. */

// ---------------------------------------------------------------------------- // User settings // ----------------------------------------------------------------------------

/**

* The text to display.
*
* This value can be any text. The entire value should be surrounded by double
* quotes. If you wish to display double quotes in your text, you have to add a
* backslash (\) in front of the double quotes character.
*/

string TEXT = "Edit this text inside the script!";

/**

* The color of the floating text.
*
* The value ranges between <1.0, 1.0, 1.0> (white) and <0.0, 0.0, 0.0> (black).
* Each number corresponds to the strength of Red, Green and Blue within the
* displayed color, respectively.
*/

vector FLOATING_TEXT_COLOR = <1.0 , 1.0, 1.0>;

/**

* The transparancy of the floating text.
*
* The value ranges between 0.0 (invisible) to 1.0 (fully visible).
*/

float FLOATING_TEXT_ALPHA = 1.0;

// ---------------------------------------------------------------------------- // Do not edit below this line unless you know what you're doing! // ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------

default { state_entry() { llSetText(TEXT, FLOATING_TEXT_COLOR, FLOATING_TEXT_ALPHA); } } </lsl>

Pose stand (simple)

<lsl> // [*TA*] Simple Pose Stand script // By Tutti Anatine // Last edit: 2 March 2014 key CREATOR = "95eaa123-5b83-437e-9fe9-90def840da39";

// ----------------------------------------------------------------------------- // Legal notices // -----------------------------------------------------------------------------

// Please do not distribute this script without full permissions // Please do not require payment for this script // Please do not remove any comments from this script

// Feel free to distribute this script // Feel free to edit this script // Please describe your changes in the changelog below this comment

// ----------------------------------------------------------------------------- // Changelog // -----------------------------------------------------------------------------

// ...

// ----------------------------------------------------------------------------- // Settings // -----------------------------------------------------------------------------

// Object string SIT_TEXT = "Pose"; integer CLICK_ACTION = CLICK_ACTION_TOUCH;

// Pose string ANIMATION_NAME = "turn_180"; float HEIGHT_OFFSET = 0.3;

// Auto Clean integer AUTO_CLEAN = FALSE; float AUTO_CLEAN_TIME = 180.f;

// ----------------------------------------------------------------------------- // Start of script // -----------------------------------------------------------------------------

integer minutes; vector object_size; key avatar; vector avatar_size; vector sit_target_position;

// -----------------------------------------------------------------------------

// Reset all avatar-related values reset_avatar() { avatar = NULL_KEY; avatar_size = ZERO_VECTOR; }

// Update the sit target update_sit_target() { sit_target_position = <0.0, 0.0, object_size.z / 2 + avatar_size.z / 2 + HEIGHT_OFFSET>; llSitTarget(sit_target_position, ZERO_ROTATION); update_avatar_position(); }

// Move the avatar sitting on the object to the correct position update_avatar_position() { integer avatar_link_number = get_link_number(avatar); if (avatar_link_number) { llSetLinkPrimitiveParams(avatar_link_number, [PRIM_POSITION, sit_target_position]); } }

// Get the link number of the child prim with given key integer get_link_number(key looking_for_key) { integer link_number = llGetNumberOfPrims();

do { if (llGetLinkKey(link_number) == looking_for_key) { return link_number; } link_number--; } while (link_number > 1);

return FALSE; }

// -----------------------------------------------------------------------------

default { state_entry() { llSetSitText(SIT_TEXT); llSetClickAction(CLICK_ACTION);

minutes = llFloor(AUTO_CLEAN_TIME / 60.f); object_size = llGetScale();

reset_avatar(); update_sit_target(); }

changed(integer change) { if (change & CHANGED_LINK) { // Number of links in object changed avatar = llAvatarOnSitTarget(); if (avatar != NULL_KEY) { // An avatar is sitting on the object avatar_size = llGetAgentSize(avatar); llRequestPermissions(avatar, PERMISSION_TRIGGER_ANIMATION); } else { // No avatar is sitting on the object reset_avatar(); if (AUTO_CLEAN) { // Set timer for automatic object deletion llSetTimerEvent(AUTO_CLEAN_TIME); llOwnerSay("I will delete myself in about " + (string) minutes + " minutes unless you sit on me again."); } } } else if (change & CHANGED_SCALE) { // Object size changed object_size = llGetScale(); update_sit_target(); } }

run_time_permissions(integer perm) { if (perm & PERMISSION_TRIGGER_ANIMATION) { // Start animation llStartAnimation(ANIMATION_NAME); update_sit_target(); } }

timer() { // Automatically delete the object llOwnerSay("I will delete myself now."); llDie(); } }

// ----------------------------------------------------------------------------- // End of script // ----------------------------------------------------------------------------- </lsl>

Documentation

TODO

Pose stand (with buttons)

A single-animation pose stand which can pose one avatar at a time. It is compatible with the optional button scripts provided here as well.

Base

The base of the pose stand, where a single avatar can sit on. Inexperienced-user-friendly options include:

  • Turn automatic deletion of the object on or off;
  • Set the timeout for automatic deletion in seconds;
  • Set the amount of meters an avatar will move up or down when the height offset buttons are used;
  • Set the number of degrees an avatar will turn clockwise or anti-clockwise when the rotation buttons are used.

More experienced users may want to change the init() function to make the script more suitable for their stand. Please read the comments in this function for instructions.

<lsl> /**

* [*TA*] Pose Stand script
* 
* This is the base for a pose stand with optional buttons.
* This stand is meant for fitting purposes only.
*
* Feel free to edit this code but do not sell it for more than L$0. Keep permissions copy/mod/transfer at all times.
* Make a copy of the original script before editing the code to prevent loss.
* If you have been charged money for this script, please contact the seller and ask for full compensation.
* Please do not remove this text and other comments within this script.
*
* @author Tutti Anatine
* @since 6 August 2011
* @version 3 June 2012
*/

key CREATOR = "95eaa123-5b83-437e-9fe9-90def840da39";

// -------------------- // settings - edit these to suit your taste // --------------------

// constants integer AUTO_DELETE = FALSE; // turns auto deletion of the object on or off | TRUE for on, FALSE is off integer TIMEOUT = 60; // the number of seconds that have to pass before the object deletes itself (when AUTO_DELETE = TRUE) float HEIGHT_OFFSET = 0.1; // the amount of meters the posing avatar moves up or down when touching the height buttons | has to be a positive number | default: 0.1 float ROTATION_OFFSET = 30.0; // the number of degrees the posing avatar turns around when touching the rotation buttons | has to be a positive number | default: 30.0

// -------------------- // do not edit below this line unless you know what you're doing! // --------------------

// variables string animation; string current_anim; vector current_offset; vector current_rot_in_degrees; rotation current_rot; integer being_sat_on;

/**

* Set up the script. This is used instead of llResetScript() for more granular control.
*/

init() {

 current_anim = "";
 current_offset = <0.0, 0.0, 0.01>;
 
 change_pose("T Pose");
 
 llSetSitText("Pose");
 llSetClickAction(CLICK_ACTION_SIT);

}

/**

* Changes the pose used to animate the avatar sitting on this object.
*
* @param string name The name of the pose used. This animation should be in the object's inventory. (Exception: SL's built-in pose "turn_180")
*/

change_pose(string name) {

   current_rot_in_degrees = <0.0, 0.0, 0.0>;
   if (name == "T Pose") {
     animation = "T Pose";
     current_rot_in_degrees = <0.0, 0.0, 180.0>;
   } else {
     animation = "turn_180";
   }
   
   current_rot = degrees_to_rotation(current_rot_in_degrees);
   update_sit_target(current_offset, current_rot);
   
   key avatar = llAvatarOnSitTarget();
   if (avatar != NULL_KEY) {
     llRequestPermissions(avatar, PERMISSION_TRIGGER_ANIMATION);
   }

}

/**

* Create a height offset value based on the avatar's length.
*
* @param key UUID The ID of the avatar.
*/

adjust_height_to_avatar(key UUID) {

 vector avatar_size = llGetAgentSize(UUID);
 vector object_size = llGetScale();
 
 float height_offset = avatar_size.z / 2 + object_size.z / 2;
 
 current_offset = <0.0, 0.0, height_offset>;
 update_sit_target(current_offset, current_rot);

}

/**

* Moves the avatar up and down.
*
* @param float correction
*/

update_height(float correction) {

 current_offset.z = current_offset.z + correction;
 update_sit_target(current_offset, current_rot);

}

/**

* Turns the avatar left and right.
*
* @param float correction
*/

update_rotation(float correction) {

 current_rot_in_degrees.z += correction;
 current_rot = degrees_to_rotation(current_rot_in_degrees);
 update_sit_target(current_offset, current_rot);

}

/**

* Converts a vector containing xyz-values to a rotation value.
*
* @param vector rot_in_degrees A vector containing xyz-rotation values in degrees.
* @return rotation The converted rotation value.
*/

rotation degrees_to_rotation(vector rot_in_degrees) {

 vector rot_in_radians = rot_in_degrees * DEG_TO_RAD;
 rotation rot = llEuler2Rot(rot_in_radians);
 return rot;

}

/**

* Sets / Updates the sit target moving the avatar on it if necessary.
*
* @param vector
* @param rotation
* @author Written by Strife Onizuka, size adjustment provided by Escort DeFarge
*/

update_sit_target(vector pos, rotation rot) { //Using this while the object is moving may give unpredictable results.

 llSitTarget(pos, rot); //Set the sit target
 key user = llAvatarOnSitTarget();
 if (user) // true if there is a user seated on the sittarget, if so update their position
 {
   vector size = llGetAgentSize(user);
   if (size) // This tests to make sure the user really exists.
   {
     // We need to make the position and rotation local to the current prim
     rotation localrot = ZERO_ROTATION;
     vector localpos = ZERO_VECTOR;
     if (llGetLinkNumber() > 1) // only need the local rot if it's not the root.
     {
       localrot = llGetLocalRot();
       localpos = llGetLocalPos();
     }
     pos.z += 0.4;
     integer linkNum = llGetNumberOfPrims();
     do {
       if (user == llGetLinkKey( linkNum )) // just checking to make sure the index is valid.
       {
         llSetLinkPrimitiveParams(linkNum, [PRIM_POSITION, ((pos - (llRot2Up(rot) * size.z * 0.02638)) * localrot) + localpos, PRIM_ROTATION, rot * localrot / llGetRootRotation()]);
         jump end; // cheaper but a tad slower then return
       }
     } while( --linkNum );
   }
   else
   { // It is rare that the sit target will bork but it does happen, this can help to fix it.
     llUnSit(user);
   }
 }
 @end;

}

/**

* Poses the avatar.
*/

pose() {

 being_sat_on = TRUE;
 llStopAnimation(current_anim);
 llStartAnimation(animation);
 current_anim = animation;

}

/**

* Sets the timer for automatic deletion. This will only be activated when AUTO_DELETE is TRUE.
*/

setTimer() {

 llWhisper(0, "I will delete myself in " + (string) TIMEOUT + " seconds unless someone sits on me before then.");
 llSetTimerEvent(TIMEOUT);

}

default {

 state_entry() {
   init();
 }
 
 touch_start(integer num_detected) {
   // get the name of the (child) prim that passed the touch event
   integer detected_link = llDetectedLinkNumber(0);
   string name = llToLower(llGetLinkName(detected_link));
   
   // bind an action to the name of each button, this way you can easily create buttons that perform any action you want
   if (name == "up") {
     update_height(HEIGHT_OFFSET);
   } else if (name == "down") {
     update_height(-HEIGHT_OFFSET);
   } else if (name == "clockwise") {
     update_rotation(-ROTATION_OFFSET);
   } else if (name == "anticlockwise") {
     update_rotation(ROTATION_OFFSET);
   } else if (name == "turn_180") {
     change_pose("turn_180");;
   } else if (name == "T Pose") {
     change_pose("T Pose");
   }
 }
   
 changed(integer change) {
   // this occurs when an avatars sits on the object, or stands up from it
   if (change & CHANGED_LINK) {
     key avatar = llAvatarOnSitTarget();
     
     if (avatar != NULL_KEY) {
       if (being_sat_on != TRUE) {
         llSetTimerEvent(0.0);
         current_anim = "sit";
         adjust_height_to_avatar(avatar);
         llRequestPermissions(avatar, PERMISSION_TRIGGER_ANIMATION);
       }
     } else {
       being_sat_on = FALSE;
       current_anim = "";
       if (AUTO_DELETE == TRUE) {
         setTimer();
       }
     }
   } else if (change & (CHANGED_SCALE | CHANGED_LINK | CHANGED_OWNER)) {
     init();
   }
 }
   
 run_time_permissions(integer perm) {
   // check if the avatar has given permission for animation
   if (perm & PERMISSION_TRIGGER_ANIMATION){
     pose();
   }
 }
   
 on_rez(integer start_param) {
   init();
 }
   
 timer() {
   llDie(); // delete the entire object
 }

} </lsl>

Buttons

Any prim you link to the pose stand is handled like a button. The script will respond to the name of a prim when that prim is touched, this happens in the touch_start event. On default, six button names are supported:

  • up | this moves the avatar up
  • down | this moves the avatar down
  • clockwise | this rotates the avatar clockwise
  • anticlockwise | this rotates the avatar anti-clockwise
  • turn_180 | this switches to animation "turn_180"
  • T Pose | this switches to animation "T Pose"

You can also create new buttons. In this case you will have to find a unique name for your new button and then write a handler in the ' if-else if' code inside touch_start. You may have to rewrite other parts of the script as well, so much sure you know have made a back-up of previously changes.

Notes

  • The function UpdateSitTarget() is written by Strife Onizuka and Escort DeFarge and was taken from LlSitTarget.
  • The in-world original uses a pose called 'T Pose' by Adenoraque Qin. It's also possible to change the script to use Linden Labs' default animation, turn_180. Just change change_pose("T Pose"); into change_pose("turn_180"); in the function init() close to the top of the script.

Known issues

  • Sometimes, the animation will get bugged when changing clothes or attachements while standing on the pose stand. I'm still investigating what this is caused by.

Tip jar

<lsl> // [*TA*] Tip Jar // Version: 1.0 // Last edit: 19 february 2013

// ---------------------------------------------------------------------------- // Script information // ----------------------------------------------------------------------------

string CREATOR = "tutti.anatine"; key CREATOR_ID = "95eaa123-5b83-437e-9fe9-90def840da39";

// ---------------------------------------------------------------------------- // License // ----------------------------------------------------------------------------

/* This script is provided free of charge. If you have paid for it, ask for a

* refund. You are free to use the code for your own purposes. However, if you
* plan to redistribute this script, keep this header in place and intact. Also
* keep the script full-perm. */

// ---------------------------------------------------------------------------- // User settings // ----------------------------------------------------------------------------

/**

* The values of the quick page buttons.
*
* Set the value to PAY_HIDE to hide a button. The values are displayed in the
* following order: top left, top right, bottom left, bottom right.
*/

list QUICK_PAY_BUTTONS = [5, 10, 20, 40];

/**

* The color of the floating text.
*
* The value ranges between <1.0, 1.0, 1.0> (white) and <0.0, 0.0, 0.0> (black).
* Each number corresponds to the strength of Red, Green and Blue within the
* displayed color, respectively.
*/

vector FLOATING_TEXT_COLOR = <1.0 , 1.0, 1.0>;

/**

* The transparancy of the floating text.
*
* The value ranges between 0.0 (invisible) to 1.0 (fully visible).
*/

float FLOATING_TEXT_ALPHA = 1.0;

/**

* The private message which will be sent to a donator after a donation.
*/

string THANK_MESSAGE = "Thank you for your donation, I really appreciate it!";

// ---------------------------------------------------------------------------- // Do not edit below this line unless you know what you're doing! // ----------------------------------------------------------------------------

key owner; string owner_name; integer donations; integer donated; integer last_donation; string last_donator;

// ----------------------------------------------------------------------------

/**

* Set base variables and then run all setup functions.
*/

init() {

   owner = llGetOwner();
   owner_name = llKey2Name(owner);
   donations = 0;
   donated = 0;
   
   llSetPayPrice(0, QUICK_PAY_BUTTONS);
   llSetClickAction(CLICK_ACTION_PAY);
   update_text();

}

/**

* Update the floating text above the tip jar to display the latest donation
* information.
*/

update_text() { string text = owner_name + "'s Tip Jar" +

       	"\nClick me to donate";
   if (donated > 0) {
       set_text(text +
       	"\nL$" + (string) donated + " donated this session" +
       	"\nLast donation: L$" + (string) last_donation+" by " + last_donator +
       	"\nAverage donation: L$" + (string) llRound(donated / donations));
   } else {
       set_text(text);
   }

}

/**

* Display a floating text above the object.
*
* @param message The text to display.
*/

set_text(string message) { llSetText(message, FLOATING_TEXT_COLOR, FLOATING_TEXT_ALPHA); }

// ----------------------------------------------------------------------------

default {

   state_entry() {
       init();
   }
   
   money(key id, integer amount) {
   	donations++;
       donated += amount;
       last_donation = amount;
       last_donator = llKey2Name(id);
       update_text();
       llInstantMessage(id, THANK_MESSAGE);
       llInstantMessage(owner, last_donator + " just donated L$" + (string) last_donation);
   }
   
   changed(integer change) {
       if (change & CHANGED_OWNER) {
           init();
       }
   }
   
   on_rez(integer start_param) {
       init();
   }

} </lsl>

Unpacker

Static

<lsl> // [*TA*] Unpacker (Static) // Version: 1.0 // Last edit: 19 february 2013

// ---------------------------------------------------------------------------- // Script information // ----------------------------------------------------------------------------

string CREATOR = "tutti.anatine"; key CREATOR_ID = "95eaa123-5b83-437e-9fe9-90def840da39";

/* This is a single unpacker script, which can be used to unpack all kinds of

* items. The containing object will display a floating text above itself, which
* informs the user about its status.
*
* The script is not capable of handing out no-copy items. When the package
* contains such items, the owner will be notified of this.
*
* This is the static edition of the unpacker script by Tutti Anatine. The
* following editions are available:
* 1. Static; A stand-alone unpacker for static objects, which will unpack when
*    touched.
* 2. Bag; An unpacker to use within wearable bags. It will unpack automatically
*    when worn. It will also activate a bag holding pose. */

// ---------------------------------------------------------------------------- // License // ----------------------------------------------------------------------------

/* This script is provided free of charge. If you have paid for it, ask for a

* refund. You are free to use the code for your own purposes. However, if you
* plan to redistribute this script, keep this header in place and intact. Also
* keep the script full-perm. */

// ---------------------------------------------------------------------------- // User settings // ----------------------------------------------------------------------------

/**

* Whether to display floating text.
*
* The value can be either TRUE or FALSE.
*/

integer FLOATING_TEXT = TRUE;

/**

* The color of the floating text.
*
* The value ranges between <1.0, 1.0, 1.0> (white) and <0.0, 0.0, 0.0> (black).
* Each number corresponds to the strength of Red, Green and Blue within the
* displayed color, respectively.
*/

vector FLOATING_TEXT_COLOR = <1.0 , 1.0, 1.0>;

/**

* The transparancy of the floating text.
*
* The value ranges between 0.0 (invisible) to 1.0 (fully visible).
*/

float FLOATING_TEXT_ALPHA = 1.0;

/**

* The name of the folder that will be created in the user's inventory.
*
* If left empty, the name of the object containing this script will be used.
*/

string FOLDER_NAME = "";

/**

* A piece of text to add in front of the folder name.
*
* This will be added even when FOLDER_NAME is not set.
*/

string FOLDER_NAME_PREFIX = "";

/**

* Whether the package should be automatically deleted after being unpacked.
*
* The value can be either TRUE or FALSE.
*/

integer AUTO_DESTRUCT = FALSE;

// ---------------------------------------------------------------------------- // Do not edit below this line unless you know what you're doing! // ----------------------------------------------------------------------------

key owner; list inventory; string folder_name;

// ----------------------------------------------------------------------------

/**

* Set base variables and then run all setup functions.
*/

init() { owner = llGetOwner(); list_inventory(); set_folder_name(); }

/**

* Assemble a list containing the name of each item inside the object's
* inventory, exclude the name of this script.
*/

list_inventory() { set_text("Reloading inventory, please be patient while waiting."); integer count = llGetInventoryNumber(INVENTORY_ALL); inventory = [];

integer i = 0; integer no_copy = FALSE; for (i; i < count; i++) { string name = llGetInventoryName(INVENTORY_ALL, i); if (llGetInventoryPermMask(name, MASK_OWNER) & PERM_COPY) { inventory += [name]; } else { no_copy = TRUE; } }

integer i_script = llListFindList(inventory, [llGetScriptName()]); inventory = llDeleteSubList(inventory, i_script, i_script);

if (no_copy == TRUE) { llInstantMessage(owner, "This package contains no-copy items, the unpacker script cannot hand these out."); }

if (llGetListLength(inventory) == 0) { set_text("This package is empty."); } else { set_text("Please touch to unpack me."); } }

/**

* Set the name of the folder that will be created in the user's inventory.
*/

set_folder_name() { if (FOLDER_NAME != "") { folder_name = FOLDER_NAME; } else { folder_name = llGetObjectName(); }

if (FOLDER_NAME_PREFIX != "") { folder_name = FOLDER_NAME_PREFIX + " " + folder_name; } }

/**

* Pass the contents of this package to the user, if that user is the owner of
* the object.
*
* @param avatar The UUID key of the avatar to give the contents to.
*/

give_inventory(key avatar) { if (!llGetListLength(inventory) == 0) { if (avatar == owner) { llGiveInventoryList(owner, folder_name, inventory); llInstantMessage(avatar, "The package was succesfully unpacked." + "You can find the contents of the package inside your inventory in a folder called \"" + folder_name + "\"."); set_text("This package is now unpacked and can be safely deleted."); } else { llInstantMessage(avatar, "I cannot give you the contents of this package, because you are not its owner."); } } else { llInstantMessage(avatar, "This package is empty, no items were given."); } auto_destruct(); }

/**

* Display a floating text above the object.
*
* @param message The text to display.
*/

set_text(string message) { if (FLOATING_TEXT == TRUE) { llSetText(message, FLOATING_TEXT_COLOR, FLOATING_TEXT_ALPHA); } }

/**

* Delete the object.
*/

auto_destruct() { if (AUTO_DESTRUCT == TRUE) { llInstantMessage(owner, "I will now automatically destruct myself."); llDie(); } }

// ----------------------------------------------------------------------------

default { state_entry() { init(); }

touch_start(integer num_detected) { give_inventory(llDetectedKey(0)); }

changed(integer change) { if (change & CHANGED_INVENTORY || change & CHANGED_ALLOWED_DROP) { list_inventory(); } else if (change & CHANGED_OWNER) { init(); } }

on_rez(integer start_param) { init(); } } </lsl>

Bag

<lsl> // [*TA*] Unpacker (Bag) // Version: 1.0 // Last edit: 19 february 2013

// ---------------------------------------------------------------------------- // Script information // ----------------------------------------------------------------------------

string CREATOR = "tutti.anatine"; key CREATOR_ID = "95eaa123-5b83-437e-9fe9-90def840da39";

/* This is a single unpacker script, which can be used to unpack all kinds of

* items. The contents will be automatically unpacked when the package is worn.
* The user can also touch the unpack to manually unpack the package. The
* containing object can display a floating text above itself, which informs the
* user about its status.
*
* The script is not capable of handing out no-copy items. When the package
* contains such items, the owner will be notified of this.
*
* This is the bag edition of the unpacker script by Tutti Anatine. The
* following editions are available:
* 1. Static; A stand-alone unpacker for static objects, which will unpack when
*    touched.
* 2. Bag; An unpacker to use within wearable bags. It will unpack automatically
*    when worn. It will also activate a bag holding pose. */

// ---------------------------------------------------------------------------- // License // ----------------------------------------------------------------------------

/* This script is provided free of charge. If you have paid for it, ask for a

* refund. You are free to use the code for your own purposes. However, if you
* plan to redistribute this script, keep this header in place and intact. Also
* keep the script full-perm. */

// ---------------------------------------------------------------------------- // User settings // ----------------------------------------------------------------------------

/**

* The name of the bag holding animation.
*/

string ANIM_NAME = "holding-bag";

/**

* Whether to display floating text.
*
* The value can be either TRUE or FALSE.
*/

integer FLOATING_TEXT = FALSE;

/**

* The color of the floating text.
*
* The value ranges between <1.0, 1.0, 1.0> (white) and <0.0, 0.0, 0.0> (black).
* Each number corresponds to the strength of Red, Green and Blue within the
* displayed color, respectively.
*/

vector FLOATING_TEXT_COLOR = <1.0 , 1.0, 1.0>;

/**

* The transparancy of the floating text.
*
* The value ranges between 0.0 (invisible) to 1.0 (fully visible).
*/

float FLOATING_TEXT_ALPHA = 1.0;

/**

* The name of the folder that will be created in the user's inventory.
*
* If left empty, the name of the object containing this script will be used.
*/

string FOLDER_NAME = "";

/**

* A piece of text to add in front of the folder name.
*
* This will be added even when FOLDER_NAME is not set.
*/

string FOLDER_NAME_PREFIX = "";

/**

* Whether the package should be automatically deleted after being unpacked.
*
* The value can be either TRUE or FALSE.
*/

integer AUTO_DESTRUCT = FALSE;

// ---------------------------------------------------------------------------- // Do not edit below this line unless you know what you're doing! // ----------------------------------------------------------------------------

key owner; list inventory; string folder_name;

// ----------------------------------------------------------------------------

/**

* Set base variables and then run all setup functions.
*/

init() { owner = llGetOwner(); list_inventory(); set_folder_name(); }

/**

* Assemble a list containing the name of each item inside the object's
* inventory, exclude the name of this script and the holding bag animation.
*/

list_inventory() { set_text("Reloading inventory, please be patient while waiting."); integer count = llGetInventoryNumber(INVENTORY_ALL); inventory = [];

integer i = 0; integer no_copy = FALSE; for (i; i < count; i++) { string name = llGetInventoryName(INVENTORY_ALL, i); if (llGetInventoryPermMask(name, MASK_OWNER) & PERM_COPY) { inventory += [name]; } else { no_copy = TRUE; } }

integer i_script = llListFindList(inventory, [llGetScriptName()]); inventory = llDeleteSubList(inventory, i_script, i_script); integer i_anim = llListFindList(inventory, [ANIM_NAME]); inventory = llDeleteSubList(inventory, i_anim, i_anim);

if (no_copy == TRUE) { llInstantMessage(owner, "This package contains no-copy items, the unpacker script cannot hand these out."); }

if (llGetListLength(inventory) == 0) { set_text("This package is empty."); } else { set_text("Please touch to unpack me."); } }

/**

* Set the name of the folder that will be created in the user's inventory.
*/

set_folder_name() { if (FOLDER_NAME != "") { folder_name = FOLDER_NAME; } else { folder_name = llGetObjectName(); }

if (FOLDER_NAME_PREFIX != "") { folder_name = FOLDER_NAME_PREFIX + " " + folder_name; } }

/**

* Pass the contents of this package to the user, if that user is the owner of
* the object.
*
* @param avatar The UUID key of the avatar to give the contents to.
*/

give_inventory(key avatar) { if (!llGetListLength(inventory) == 0) { if (avatar == owner) { llGiveInventoryList(owner, folder_name, inventory); llInstantMessage(avatar, "The package was succesfully unpacked." + "You can find the contents of the package inside your inventory in a folder called \"" + folder_name + "\"."); set_text("This package is now unpacked and can be safely deleted."); } else { llInstantMessage(avatar, "I cannot give you the contents of this package, because you are not its owner."); } } else { llInstantMessage(avatar, "This package is empty, no items were given."); } auto_destruct(); }

/**

* Display a floating text above the object.
*
* @param message The text to display.
*/

set_text(string message) { if (FLOATING_TEXT == TRUE) { llSetText(message, FLOATING_TEXT_COLOR, FLOATING_TEXT_ALPHA); } }

/**

* Delete the object.
*/

auto_destruct() { if (AUTO_DESTRUCT == TRUE) { llInstantMessage(owner, "I will now automatically destruct myself."); llDie(); } }

// ----------------------------------------------------------------------------

default { state_entry() { init(); }

touch_start(integer num_detected) { give_inventory(llDetectedKey(0)); }

attach(key id) { if (id != NULL_KEY) { llRequestPermissions(id, PERMISSION_TRIGGER_ANIMATION); give_inventory(id); } else { llStopAnimation(ANIM_NAME); } }

run_time_permissions(integer perm) { if (perm == PERMISSION_TRIGGER_ANIMATION) { llStartAnimation(ANIM_NAME); } }

changed(integer change) { if (change & CHANGED_INVENTORY || change & CHANGED_ALLOWED_DROP) { list_inventory(); } else if (change & CHANGED_OWNER) { init(); } }

on_rez(integer start_param) { init(); } } </lsl>