Difference between revisions of "User:Kimm Paulino/Scripts"
Kimm Paulino (talk | contribs) m (→Phantom Prim Setter: Added some usage notes) |
Kimm Paulino (talk | contribs) (Added simple example to show reading HTML from a notecard for displaying on a prim) |
||
Line 1,300: | Line 1,300: | ||
llSetTimerEvent (0.0); | llSetTimerEvent (0.0); | ||
} | } | ||
} | |||
</lsl> | |||
== HTML Page from a notecard on a prim == | |||
<lsl> | |||
// Example to show how a basic HTML page can be read | |||
// from a notecard and then displayed on the face of a prim. | |||
// | |||
// Kimm Paulino, Jan 2011 | |||
// Note that the HTML can only be 1024 characters long - no more! | |||
integer HTML_FACE_DISPLAY = 2; // See http://wiki.secondlife.com/wiki/Face | |||
string HTML_DEFAULT = "<head><style>body {background-color:#808000;}</style></head><body><h1>HTML on a Prim Example</h1><p align=\"centre\">Kimm Paulino, Jan 2011</p></body>"; | |||
// Used when processing notecard-based questions only | |||
string HTML_NOTECARD_NAME="htmlpage"; | |||
key gNCQueryId; | |||
integer gNCLine; | |||
string gHtmlPage=""; | |||
check_notecards() | |||
{ | |||
integer nccount = llGetInventoryNumber (INVENTORY_NOTECARD); | |||
integer i; | |||
string htmlnote; | |||
gHtmlPage = ""; | |||
for (i=0 ; i<nccount ; i++) | |||
{ | |||
htmlnote = llGetInventoryName (INVENTORY_NOTECARD, i); | |||
if (htmlnote == HTML_NOTECARD_NAME) | |||
{ | |||
gNCLine = 0; | |||
gNCQueryId = llGetNotecardLine (HTML_NOTECARD_NAME, gNCLine); | |||
return; | |||
} | |||
} | |||
// if got here then there is no notecard with HTML info in it, | |||
// so show the current page and return | |||
show_html_page(); | |||
} | |||
show_html_page () | |||
{ | |||
string html = "data:text/html,"; | |||
if (gHtmlPage != "") | |||
{ | |||
html += llEscapeURL (gHtmlPage); | |||
} | |||
else | |||
{ | |||
html += llEscapeURL (HTML_DEFAULT); | |||
} | |||
// note that this is limited to 1024 characters | |||
// See: http://wiki.secondlife.com/wiki/User:Kelly_Linden/lsl_hacks | |||
llOwnerSay ("HTML Page Size: " + (string)llStringLength (html)); | |||
llSetPrimMediaParams(HTML_FACE_DISPLAY, // Side to display the media on. | |||
[PRIM_MEDIA_AUTO_PLAY,TRUE, // Show this page immediately | |||
PRIM_MEDIA_CURRENT_URL,html, // The url currently showing | |||
PRIM_MEDIA_HOME_URL,html, // The url if they hit 'home' | |||
PRIM_MEDIA_HEIGHT_PIXELS,512, // Height/width of media texture will be | |||
PRIM_MEDIA_WIDTH_PIXELS,512, // rounded up to nearest power of 2. | |||
PRIM_MEDIA_AUTO_SCALE, FALSE, | |||
PRIM_MEDIA_CONTROLS, PRIM_MEDIA_CONTROLS_MINI | |||
]); | |||
} | |||
default | |||
{ | |||
on_rez (integer start_param) | |||
{ | |||
llResetScript(); | |||
} | |||
state_entry () | |||
{ | |||
check_notecards(); | |||
} | |||
dataserver (key query_id, string data) | |||
{ | |||
if (query_id != gNCQueryId) | |||
{ | |||
// Not for us | |||
return; | |||
} | |||
if (data == EOF) | |||
{ | |||
// All done | |||
show_html_page(); | |||
return; | |||
} | |||
// Otherwise add the data from the notecard to the CSS string | |||
gHtmlPage += data; | |||
// and read the next line | |||
gNCLine++; | |||
gNCQueryId = llGetNotecardLine(HTML_NOTECARD_NAME, gNCLine); | |||
} | |||
changed (integer change) | |||
{ | |||
if (change & CHANGED_INVENTORY) | |||
{ | |||
check_notecards(); | |||
} | |||
} | |||
} | } | ||
</lsl> | </lsl> |
Revision as of 12:52, 13 January 2011
Simple Teleport
<lsl> // Teleports on 'left click' (touch). // // Set the coordinates to tp to in the DEST= line after these comments. // // Kimm Paulino // Sept 2009 // // Teleport bit based on the following: // // Copyright (c) 2008, Scripting Your World // All rights reserved. // // Scripting Your World // By Dana Moore, Michael Thome, and Dr. Karen Zita Haigh // http://syw.fabulo.us // http://www.amazon.com/Scripting-Your-World-Official-Second/dp/0470339837/ // // You are permitted to use, share, and adapt this code under the // terms of the Creative Commons Public License described in full // at http://creativecommons.org/licenses/by/3.0/legalcode. // That means you must keep the credits, do nothing to damage our // reputation, and do not suggest that we endorse you or your work. // // Listing 2.4: Teleport3 - Optimized Intrasim Teleport
vector DEST = <224, 213, 22>; // the teleport will be to this location vector SITPOS = <0,0,0.5>; key gAv;
moveTo(vector origin, vector destination ) { // removed jumpdist
float dist = llVecDist(origin, destination); integer passes = llCeil( llLog(dist/10.0) / llLog(2.0) ); integer i; list params = [PRIM_POSITION, destination]; for (i=0; i<passes; i++) { params = (params=[]) + params + params; } llSetPrimitiveParams(params);
}
teleport(key av) {
if (av == NULL_KEY) { return; }
vector origin = llGetPos(); llSetAlpha (0.0, ALL_SIDES); moveTo(origin, DEST); // no need to sleep -- llSetPrimParams has 0.2s delay llUnSit(av); moveTo(DEST, origin); llSetAlpha (1.0, ALL_SIDES);
}
default {
state_entry() { llSetClickAction (CLICK_ACTION_SIT); llSitTarget(SITPOS, ZERO_ROTATION); }
changed(integer changebits) { if (changebits & CHANGED_LINK) { gAv = llAvatarOnSitTarget(); if (gAv != NULL_KEY) { teleport(gAv); } } }
} </lsl>
Die after 10 minutes
<lsl> // This will kill the prim it is in, 10 minutes after the prim has been rezzed. // // Kimm Paulino, June 2010
default {
state_entry() { // 10 minutes llSetTimerEvent (600.0); } timer () { llSetTimerEvent (0.0); llDie(); }
} </lsl>
Simple Prim Texture Changer
<lsl> // Texture changer. Will cycle through all textures // in the prims inventory. // // Kimm Paulino // Written for Bebelou Naidoo, April 2010
float TIMER_PERIOD = 5.0; // in seconds. integer RANDOM = 1; // 0 = sequential, 1 = random
// globals integer gNumTextures; integer gLastTexture;
default {
on_rez (integer n) { llResetScript(); }
state_entry() { gLastTexture = 0; gNumTextures = llGetInventoryNumber(INVENTORY_TEXTURE); llSetTimerEvent(TIMER_PERIOD); } timer() { integer nextTexture; if (RANDOM) { // Randomly choose one nextTexture = (integer)llFrand (gNumTextures); } else { // Move on from the last one gLastTexture ++; if (gLastTexture > gNumTextures) { gLastTexture = 0; } nextTexture = gLastTexture; } string texture = llGetInventoryName(INVENTORY_TEXTURE, nextTexture); llSetTexture(texture, ALL_SIDES); }
} </lsl>
Phantom Prim Setter
<lsl> // Script to make a prim appear to be phantom, so it can be linked, etc, // without actually being phantom. // // Procedure: // 1. Create prim (not linked to anything) // 2. Put this script in it and let it run // 3. Link the prim to everything else // 4. Delete the script if you like (no reason to keep it) // // Basic technique found on the SL forums. // // Kimm Paulino // Written for Synonyme Toll, May 2010
default { on_rez (integer start_param) { llResetScript(); }
state_entry() { // This makes the prim appear to be 'phantom like' without // actually making it phantom ... llVolumeDetect(TRUE);
// Now disable the script once we've done our job llSetScriptState(llGetScriptName(), FALSE); } } </lsl>
Floating Script
<lsl> // Script for applying a small, random impulse to physical objects // to make them float about. // // Note: There is no bound to where the objects go, this script // is meant for objects in a physically bounded area (like a fish tank with a lid) // // Feel free to use as you wish, but do let me know if you find something // interesting to do with it ... // // Kimm Paulino // Written for Synonyme Toll, Sept 2009 //
// How often to apply the new impulse (in seconds) float IMPULSE_TIME = 3.0;
// Range for the magnitude and x, y, z directions of the impulse float IMPULSE_RANGE = 0.5;
float newRand (float range) {
// Want a random number in range +- 'range' return (llFrand(range) - (range/2.0));
}
applySmallImpulse () {
vector my_vec = llGetVel(); float my_mag = llVecMag (my_vec); vector my_dir = llVecNorm (my_vec);
// Change the magnitude slightly ... my_mag = my_mag + newRand(IMPULSE_RANGE); // Change the direction slightly too ... my_dir = <my_dir.x + newRand(IMPULSE_RANGE), my_dir.y + newRand(IMPULSE_RANGE), my_dir.z + newRand(IMPULSE_RANGE)>; vector new_vec = my_dir * my_mag; //llOwnerSay ("Applying Impulse: " + (string)new_vec);
// apply the impulse to us llApplyImpulse (new_vec, TRUE);
}
default {
on_rez(integer n) { llResetScript (); } state_entry() { // Turn on Physics llSetPrimitiveParams ([PRIM_PHYSICS, TRUE]); // Set the boyancy to > 1.0 so that it floats llSetBuoyancy (1.0); // Set up a timer to apply an impulse llSetTimerEvent (IMPULSE_TIME); } timer() { applySmallImpulse(); }
} </lsl>
Sit and Play Animation
<lsl> // Sets a sit target and plays whatever animation is // stored in the contents on the prim when someone sits down. // // Kimm Paulino // Written for Synonyme Toll, April 2010
// These numbers are totally dependent on the object containing // the script, and possibly even the animation to be used too. vector gPosition = <0.0, 0.0, 0.1>; vector gRotation = <0.0, 0.0, 0.0>; // in degrees
default {
on_rez (integer start_param) { llResetScript(); } state_entry() { // These numbers are totally dependent on the object containing the script! llSitTarget (gPosition, llEuler2Rot (gRotation * DEG_TO_RAD)); }
changed (integer change) { // When someone sits on an object, the av is considered to be // a linked-in child prim, so the link number changes if (change & CHANGED_LINK) { key av = llAvatarOnSitTarget(); if (av) { // yes so need to play the animation // first request permissions - results in the callback ... llRequestPermissions(av, PERMISSION_TRIGGER_ANIMATION); } } } run_time_permissions(integer perms) { // Do we have permissions to run the animations? // results in the timer! if(perms) { llSetTimerEvent(1.0); } else { llSetTimerEvent(0.0); } } timer() { key av = llAvatarOnSitTarget(); // If the av is still sitting, play the animation stored in the prim if (av) { llStopAnimation("sit"); llStartAnimation( llGetInventoryName( INVENTORY_ANIMATION, 0 )); } else { // stop playing the animations for sitting if the av // is no longer sitting ... llStopAnimation("sit_generic"); } }
} </lsl>
Move up and down
<lsl> // Makes an object go up or down, gradually, when touched. // In order to make it a gradual movement, it uses a physical // object, which means it might be a little wobbly ... // // Kimm Paulino // Written for Nicollette Arabello, November 2009 //
integer gState; integer DOWN=0; integer GO_UP=1; integer UP=2; integer GO_DOWN=3;
float MOVE_DAMPING=4.0; float MOVE_TIME=4.0;
// Move to 1m above starting position vector gOffset = <0.0, 0.0, 1.0>; vector gStartPosition;
default {
state_entry() { // Use physics so that can use MoveToTarget gStartPosition = llGetPos (); // Stop the object rotating llSetStatus(STATUS_ROTATE_X|STATUS_ROTATE_Y|STATUS_ROTATE_Z, FALSE); llSetStatus(STATUS_PHYSICS, TRUE); llMoveToTarget (gStartPosition, MOVE_DAMPING); llSetTimerEvent (MOVE_TIME); } on_rez(integer n) { llResetScript(); }
timer () { if (gState == DOWN) { gState = GO_UP; llMoveToTarget(gStartPosition + gOffset, MOVE_DAMPING); gState = UP; } else if (gState == UP) { gState = GO_DOWN; llMoveToTarget(gStartPosition, MOVE_DAMPING); gState = DOWN; } }
} </lsl>
Simple Door
<lsl> // Was asked to modify an existing door script. I was just given // this to update so afraid I can't give credit, but if you know where // the original came from, do let me know. // // Swinging door LSL script #1 // Handles the touch event. // Handles the collision event. // Handles closing the door automatically via a timer event. // Triggers sounds when the door opens or closes. // // Updated by Kimm Paulino, April 2010, for Synonyme Toll, // to use local coords for the rotations instead of global // coords. // // Updated by Kimm Paulino, May 2010, for Synonyme Toll, // to use messages to link two doors together. // // Note: For this to work, you need a door with a path cut // of 0.375 to 0.875 and a width that is twice as wide // as you need (the cut brings it back down to the right // size). Without this, it will just rotate on the spot, // not swing at the edge. //
// Parameters you might want to change
float delay = 3.0; // time to wait before automatically closing door
// set to 0.0 to not automatically close
float direction = 1.0; // set to 1.0 or -1.0 to control direction the door swings float volume = 0.5; // 0.0 is off, 1.0 is loudest integer linked_doors = 0; // set to 1 if using this script in a linked set of doors float angle = 90.0; // angle to open in degrees integer coll_detect = 1; // set to 1 to make door open on approach
// Variables you will most likely leave the same
key open_sound = "cb340647-9680-dd5e-49c0-86edfa01b3ac"; key close_sound = "e7ff1054-003d-d134-66be-207573f2b535";
// KP: Link send/recv functions // IMPORTANT // If you only have one pair of doors using this script, then // these will be fine. If you have several doors using this // script that you don't want all opening together, then // you'll have to change these to something unique. // integer LINK_MSG_NUM = 452345; string LINK_MSG_STR_OPEN = "Open the door"; string LINK_MSG_STR_CLOSE = "Close the door";
linkSend (string dir) {
if (linked_doors) { // Note: Could try to work out the link numbers of the doors, // but simplest is to send to all other prims in the build. llMessageLinked (LINK_ALL_OTHERS, LINK_MSG_NUM, dir, NULL_KEY); }
}
integer linkRecv (integer sender_num, integer num, string str, key id, string str_to_check) {
if (linked_doors) { // Check it really is a message for these doors if (num == LINK_MSG_NUM) { if (str == str_to_check) { // All good return 1; } } } // not for us return 0;
}
// Processing for the script when it first starts up
default {
// What we do when we first enter this state
state_entry() { state open; // Move to the open state }
}
// Processing for the script when it is in the closed state
state closed {
// What we do when we first enter this state
state_entry() { llTriggerSound(close_sound, volume); // Trigger the sound of the door closing llSetLocalRot(llEuler2Rot(<0, 0, direction * angle * DEG_TO_RAD>) * llGetLocalRot());
}
// What we do when the door is clicked (”touched”) with the mouse
touch_start(integer total_number) { linkSend (LINK_MSG_STR_OPEN); // KP: Tell everyone else we are now in the open state state open; // Move to the open state }
// What to do when something hits the door
collision_start(integer total_number) { if (coll_detect != 0) { linkSend (LINK_MSG_STR_OPEN); // KP: Tell everyone else we are now in the open state state open; // Move to the open state } } // KP: Handle link messages link_message (integer sender_num, integer num, string str, key id) { if (linkRecv (sender_num, num, str, id, LINK_MSG_STR_OPEN)) { state open; // Move to the open state } }
// What to do when the timer goes off
timer() { llSetTimerEvent(0.0); // Set the timer to 0.0 to turn it off }
}
// Processing for the script when it is in the open state
state open {
// What we do when we first enter this state
state_entry() { llTriggerSound(open_sound, volume);// Trigger the sound of the door opening llSetLocalRot(llEuler2Rot(<0, 0, -direction * angle * DEG_TO_RAD>) * llGetLocalRot());
llSetTimerEvent(delay); // Set the timer to automatically close it }
// What do do when pulling the door from Inventory if it was saved while open
on_rez(integer start_param) { state closed; }
// What we do when the door is clicked (”touched”) with the mouse
touch_start(integer total_number) { linkSend (LINK_MSG_STR_CLOSE); // KP: Tell everyone else we are now in the open state state closed; // Move to the closed state }
// What to do when something hits the door
collision_start(integer total_number) { // Do nothing, the door is already open }
// KP: Handle link messages link_message (integer sender_num, integer num, string str, key id) { if (linkRecv (sender_num, num, str, id, LINK_MSG_STR_CLOSE)) { state closed; // Move to the open state } }
// What to do when the timer goes off
timer() { llSetTimerEvent(0.0); // Set the timer to 0.0 to turn it off state closed; // Move to the closed state }
} </lsl>
Child Prim Rotations
This is a test script to pull together all the advice about rotations about a centre of rotation, in local coordinates with the appropriate work arounds for setting rotations, etc. Not really meant for use on its own but just to highlight the logic for applying the rotations.
<lsl> // Testing child prim rotations. // This is a script that could be used with a non-path-cut linked door or windows // and so on, with the correct rotation offsets and calculations to function. // // Kimm Paulino, August 2010
// Specify the rotation to apply, as a Euler vector in degrees vector gNewRotation_e = <0.0, 0.0, 150.0>;
// Specify the details of the Centre of Rotation to use. // Can either work it out from the prim parameters or put a hardcoded one in here integer gCorAxis = 1; // 1=x, 2=y, 3=z, -1=-x, -2=-y, -3=-z, 0=gCor vector gCor = <0.0,0.0,0.0>; // Relative to root prim, if gCorAxis = 0
// This test script will automatically return the prim to its start // parameters after this time if gAutoClose is set to TRUE. float RESET_TIME = 5.0; integer gAutoClose = FALSE;
vector gInitialPosition; rotation gInitialRotation; integer OPEN=0; integer CLOSED=1; integer gState;
store_child () { // llOwnerSay ("-----------------");
gInitialPosition = llGetLocalPos();
// llOwnerSay ("Initial Position: " + (string)gInitialPosition);
gInitialRotation = llGetLocalRot();
// llOwnerSay ("Initial Rotation: " + (string)r2v(gInitialRotation)); }
restore_child() {
// Note: Use the PRIM_ROTATION workaround, as described in SVC-93 llSetPrimitiveParams ([ PRIM_POSITION, gInitialPosition, PRIM_ROTATION, gInitialRotation / llGetRootRotation()]); //llSetPos (gInitialPosition); //llSetLocalRot (gInitialRotation);
}
vector calcCorAxis () {
// Note: If the prim is rotated, then we need to apply the // same rotation to the size values to pick up the correct axis vector prim_size = llGetScale() * llGetLocalRot(); if (gCorAxis == 1) { return <(prim_size.x/2.0), 0.0, 0.0>; } else if (gCorAxis == 2) { return <0.0, (prim_size.y/2.0), 0.0>; } else if (gCorAxis == 3) { return <0.0, 0.0, (prim_size.z/2.0)>; } else if (gCorAxis == -1) { return <(-prim_size.x/2.0), 0.0, 0.0>; } else if (gCorAxis == -2) { return <0.0, (-prim_size.y/2.0), 0.0>; } else if (gCorAxis == -3) { return <0.0, 0.0, (-prim_size.z/2.0)>; } else { return gCor; }
}
vector r2v (rotation r) {
return (RAD_TO_DEG * llRot2Euler (r));
}
// rot is a rotation to be applied to the prim // cor is a relative position (to the root) for the centre of rotation rotate_child (rotation rot, vector cor) {
// Work in local coordinates vector current_position = llGetLocalPos(); rotation current_orientation = llGetLocalRot(); //llOwnerSay ("Current position/rotation: " + (string)current_position + " / " + (string)r2v(current_orientation)); // Calculate the offset from the centre of the object // to the centre of rotation. This effectively moves // the object so that the cor can be thought off as the // origin. Once we've done the calculations, we'll move it back. vector normalised_position = current_position - cor; //llOwnerSay ("Normalised position/COR: " + (string)normalised_position + " / " + (string)cor); // Calculate the new position by applying the required // rotation to the current position (i.e. rotate the // vector origin-position to produce origin-newposition) vector new_normalised_position = normalised_position * rot; //llOwnerSay ("Rotated Normalised Position: " + (string)new_normalised_position); vector new_position = cor + new_normalised_position; //llOwnerSay ("New Actual Position: " + (string)new_position); rotation new_orientation = current_orientation * rot; //llOwnerSay ("New Orientation: " + (string)r2v(new_orientation)); // Set local position and rotation // Note: There is no llSetLocalPos - llSetPos will do local coords for a child //llSetPos (new_position); //llSetLocalRot (new_orientation); // However, use llSetPrimitiveParams to set both at same time, without // incurring two 0.2s delays ... although note, have to use // the PRIM_ROTATION workaround, as described in SVC-93 llSetPrimitiveParams ([ PRIM_POSITION, new_position, PRIM_ROTATION, new_orientation / llGetRootRotation()]);
}
default {
on_rez (integer start_param) { // As positions will be stored on entry, // don't want to auto reset on rez, as it might // be rezed in the closed position, which would screw // things up!
// llResetScript();
} state_entry () { // store initial position/etc gState = OPEN; store_child (); } touch_start (integer num_detected) { if (gState == OPEN) { // Need to convert CoR to local coordinates relative to // the root prim (not just relative to this prim). vector cor = llGetLocalPos() + calcCorAxis(); rotate_child (llEuler2Rot (gNewRotation_e * DEG_TO_RAD), cor); if (gAutoClose) { llSetTimerEvent (RESET_TIME); } gState = CLOSED; } else { restore_child (); gState = OPEN; } } timer () { gState = OPEN; restore_child (); llSetTimerEvent (0.0); }
} </lsl>
Prim Reducing Script
This will reduce a prim's size by scaling it by the specified number of steps and applying that scaling the specified number of times using a timer.
<lsl> // Reducing Script // // Kimm Paulino // Written for Stewart Bosatsu, Sept 2010
integer TIMER_STEPS = 18; float REDUCING_STEPS = 20; // If this >= TIMER_STEPS then prim will disappear float TIMER_INTERVAL = 2.0; // In seconds integer gCount; vector gReducingFactor;
default {
on_rez (integer start_param) { llResetScript(); } state_entry() { gCount = 0; vector size = llGetScale(); float scaling = 1.0 / REDUCING_STEPS; gReducingFactor = size * scaling; }
touch_start(integer total_number) { llSetTimerEvent (TIMER_INTERVAL); } timer () { // Reduce the size by 1/TIMER_STEPS % each time gCount ++; if (gCount > TIMER_STEPS) { // disable and quit llSetTimerEvent (0.0); llDie(); return; } // Reduce prim vector size = llGetScale(); size = size - gReducingFactor; llSetScale (size); }
} </lsl>
Simple Path Script
<lsl> // Path Setting Script // // In 'edit' mode, user moves the object and 'saves' various positions // over time, then the positions can be replayed. This either uses the physical // move functions to create smooth movement or non-physical movements // for a slightly more jerky movement! // // NOTE: Positions and rotations are relative to the region, so if you // move the prim, then the positions won't move with it - you'd have to // reset the script (using the 'reset' button) and store a new path. // // Depending on the settings, the system can either loop forever // or play just once. // // It also has the option of resetting if you change owners, which // might be useful if you want new owners to be able to store their // own paths. // // Kimm Paulino // Oct 2010
integer gDebug = FALSE; integer gPhysics = FALSE; // Set to use physical movements integer gLoop = TRUE; // Set to continually loop through the movements integer gResetOnOwnerChange = TRUE; // Set if want script to auto reset when changing owners
list gPositionData; // Always assume that there are the same numbers list gRotationData; // of position and rotation data points integer gCurrentIdx; float gTimePeriod = 2.0; float gTau = 5.0; key gOwnerId; integer gListen; integer gTimeHandle; integer gTauHandle; string gHelpMsg = "Use EDIT mode to move your object, selecting 'Save' to save each position. Select 'Done' once complete. Don't forget to save your first position too!"; string gErrorMsg = "Something unexpected went wrong, suggest you reset the script!"; string SAVE_BTN = "Save"; string DONE_BTN = "Done"; string TIME_BTN = "Time Adjust"; string TAU_BTN = "Tau Adjust"; string RESET_BTN = "Reset"; string START_BTN = "Start"; string STOP_BTN = "Stop"; string START_MSG = "start"; // What a passer by can type in via chat integer LISTEN_CH = 600; integer TIME_CH = 900; integer TAU_CH = 901;
doDebug (string msg) { if (gDebug) { llOwnerSay (msg); } }
doMove() {
integer num_points = llGetListLength(gPositionData);
if (num_points != llGetListLength (gRotationData)) { llOwnerSay (gErrorMsg); disableMove(); return; }
if (gCurrentIdx >= num_points) { if (gLoop) { // Loop around for another go gCurrentIdx = 0; } else { // All complete disableMove(); return; } }
doDebug ("Moving to position " + (string)gCurrentIdx);
vector next_pos = llList2Vector (gPositionData, gCurrentIdx);
rotation next_rot = llList2Rot (gRotationData, gCurrentIdx);
if (next_pos == ZERO_VECTOR && next_rot == ZERO_ROTATION) { // ignore }
else { if (gPhysics) { llMoveToTarget(next_pos, gTau); llLookAt(next_pos,1,1); llRotLookAt(next_rot,1,1); } else { // doDebug ("moving to: " + (string)next_pos); llSetRot (next_rot); llSetPos (next_pos); } }
// Move on to the next point gCurrentIdx ++; }
dialog () { list buttons; if (gPhysics) { buttons = [SAVE_BTN, DONE_BTN, RESET_BTN, START_BTN, STOP_BTN, TIME_BTN, TAU_BTN]; } else { buttons = [SAVE_BTN, DONE_BTN, RESET_BTN, START_BTN, STOP_BTN, TIME_BTN]; }
llDialog (gOwnerId, gHelpMsg, buttons, LISTEN_CH);
}
enableMove () { if (gPhysics) { doDebug ("Enabling physical move"); llSetStatus (PRIM_PHYSICS, TRUE); } else { doDebug ("Enabling non-physical move"); llSetStatus(PRIM_PHYSICS, FALSE); } llSetTimerEvent (gTimePeriod); gCurrentIdx = 0; doMove (); }
disableMove () { doDebug ("Disabling move"); llSetStatus (PRIM_PHYSICS, FALSE); llSetTimerEvent (0.0); }
default { on_rez (integer start_param) { // if we reset on rez, then a user can't take an object into // inventory have rerez it with the same path stored. // // Means that if they do want to clear the path, say because // the position in the Sim has changed, then they have to use // the 'reset' option. }
state_entry() { llOwnerSay ("Ready to start saving positions. Touch for menu, then go to SL Edit mode to move the object and use 'save' on the menu to save each position."); gOwnerId = llGetOwner();
}
touch_start(integer who) { gListen = llListen (LISTEN_CH,"",NULL_KEY,""); if (llDetectedKey(0) == gOwnerId) { dialog(); } else { if (!gLoop) { // Let nearby users start the moving llWhisper (0, "To start the movement, please type the following into local chat: /" + (string)LISTEN_CH + " " + START_MSG); } }
}
listen (integer channel, string name, key id, string msg) { vector pos = llGetPos(); rotation rot = llGetRot();
if (channel == LISTEN_CH) { if (msg == START_BTN || msg == START_MSG) { enableMove(); }
// non-owners can't do anything else if (id != gOwnerId) { return; }
if (msg == SAVE_BTN) { gPositionData += pos; gRotationData += rot; dialog (); } else if (msg == STOP_BTN) { disableMove(); } else if (msg == RESET_BTN) { llResetScript(); } else if (msg == TIME_BTN) { gTimeHandle = llListen (TIME_CH, "", gOwnerId, ""); llOwnerSay ("Adjust time using: /" + (string)TIME_CH + " <float seconds>"); } else if (msg == TAU_BTN) { gTauHandle = llListen(TAU_CH, "", gOwnerId, ""); llOwnerSay ("Adjust Tau using: /" + (string)TAU_CH + " <float value>"); } else if (msg == DONE_BTN) { llOwnerSay("To reset use: /" + (string)LISTEN_CH + " reset"); llOwnerSay("To start use: /" + (string)LISTEN_CH + " start"); } }
if (channel == TIME_CH) { gTimePeriod = (float)msg; llListenRemove (gTimeHandle); llOwnerSay ("Time period set to " + msg); }
if (channel == TAU_CH) { gTau = (float)msg; llListenRemove (gTauHandle); llOwnerSay ("Tau set to " + msg); } }
changed(integer ch) { if(ch & CHANGED_OWNER) { if (gResetOnOwnerChange) { // This will clear out all stored positions of course! llResetScript(); } } }
timer() { doMove(); } } </lsl>
Visitor Counter and LM Giver
<lsl> // Simple visitor counter and lm giver script // // Just finds the first landmark stored in the prim and // gives it to non-owners on touch. // // For owners, if the prim is touched, presents a simple // dialog to either list visitors or reset the visitors counts. // // Kimm Paulino // Written for Vikki Hastings, Oct 2010
// Configure the behaviour we want integer gLogDates = TRUE; // Include dates in the log integer gKeepFirst = FALSE; // Keep the first visit of someone, not the last string gFloatingText = "Click me for lm"; // Set to "" to disable vector gFloatingTextColour = <1.0, 1.0, 1.0>; float gFloatingTextAlpha = 1.0;
integer MAX_VISITORS = 30; list gVisitors; list gVisitorTimes; integer gVisitorCount; integer gChannel; integer gListenHandle;
integer getRandomChannel() {
// Some magic I got of the SL wiki somewhere ... return -llRound(llFrand( llFabs(llSin(llGetGMTclock())) * 1000000 )) - 11;
}
listVisitors () {
llOwnerSay("----------------"); llOwnerSay("Total number of visits: " + (string)gVisitorCount); if (gVisitorCount > 0) { llOwnerSay("Most recent visitors:"); integer i; integer len = llGetListLength(gVisitors); for (i=0; i < len; i++) { if (gLogDates) { llOwnerSay("[" + llList2String (gVisitorTimes, i) + "] " +llList2String(gVisitors,i)); } else { llOwnerSay(llList2String(gVisitors,i)); } } }
}
resetVisitors() {
llOwnerSay ("Resetting visitor count"); gVisitors = []; gVisitorTimes = []; gVisitorCount = 0;
}
addVisitor(string name) {
// see if we've already seen this one integer idx = llListFindList(gVisitors, (list)name); if (idx != -1 ) { // Already in the list, so: // If storing dates, then need to decide if we are keeping // the first visit or the last // // If we are not keeping dates, just don't bother logging again if (!gLogDates) { return; } if (gKeepFirst) { // Already have the entry we wish to keep ... return; } // ok, we are keeping the last visits, so remove // the name from the list and let the rest of the function // add it in again! gVisitors = llDeleteSubList (gVisitors, idx, idx); gVisitorTimes = llDeleteSubList (gVisitorTimes, idx, idx); } else { // Note: Will count people who visited before but have // now dropped off the end of the 'last visitors' list, but // thats all. gVisitorCount ++; }
//llOwnerSay ("Adding " + name); gVisitors += name; gVisitorTimes += llGetDate();
// See if we are at the end of the list // this helps to keep memory limits down // and also creates a rolling list of most // recently seen avs if (llGetListLength(gVisitors) > MAX_VISITORS) { // Remove first entry in the list gVisitors = llDeleteSubList (gVisitors, 0, 0); gVisitorTimes = llDeleteSubList (gVisitorTimes, 0, 0); }
}
default {
on_rez (integer start_param) { llResetScript(); }
state_entry() { resetVisitors(); gChannel = getRandomChannel(); llSetText (gFloatingText, gFloatingTextColour, gFloatingTextAlpha); // Range, angle, rate // So 5m range, 180 deg, every 5 secs llSensorRepeat ("", NULL_KEY, AGENT, 5.0, PI, 5.0); }
sensor(integer total_number) { key avid = llDetectedKey(0); string av = llKey2Name (avid); if (avid == llGetOwner ()) { // Don't count owner return; } addVisitor (av); } touch_start(integer total_number) { // Two things to do on touch // If its the owner, dump list of visitors // If its not, then give a lm if one exists string visitor = llDetectedKey (0); if (visitor == llGetOwner()) { llListenRemove (gListenHandle); // in case things didn't complete last time llSetTimerEvent (600.0); // In 10 minutes, all times out gListenHandle = llListen (gChannel, "", "", ""); llDialog (visitor, "Kimm's Visitor Tracker", ["List", "Reset"], gChannel); } else { // get the name of the first landmark in the inventory string lm = llGetInventoryName(INVENTORY_LANDMARK, 0); if (lm != "") { llGiveInventory(visitor, lm); } } } listen (integer channel, string name, key id, string msg) { if (channel != gChannel) { // not for us ... return; } if (msg == "List") { listVisitors(); } else if (msg == "Reset") { resetVisitors(); } llListenRemove (gListenHandle); gListenHandle = 0; } timer () { llListenRemove (gListenHandle); gListenHandle = 0; llSetTimerEvent (0.0); }
} </lsl>
HTML Page from a notecard on a prim
<lsl> // Example to show how a basic HTML page can be read // from a notecard and then displayed on the face of a prim. // // Kimm Paulino, Jan 2011
// Note that the HTML can only be 1024 characters long - no more! integer HTML_FACE_DISPLAY = 2; // See http://wiki.secondlife.com/wiki/Face
string HTML_DEFAULT = "<head><style>body {background-color:#808000;}</style></head><body>
HTML on a Prim Example
Kimm Paulino, Jan 2011
</body>";
// Used when processing notecard-based questions only string HTML_NOTECARD_NAME="htmlpage"; key gNCQueryId; integer gNCLine; string gHtmlPage="";
check_notecards() { integer nccount = llGetInventoryNumber (INVENTORY_NOTECARD); integer i; string htmlnote; gHtmlPage = "";
for (i=0 ; i<nccount ; i++) { htmlnote = llGetInventoryName (INVENTORY_NOTECARD, i); if (htmlnote == HTML_NOTECARD_NAME) { gNCLine = 0; gNCQueryId = llGetNotecardLine (HTML_NOTECARD_NAME, gNCLine); return; } }
// if got here then there is no notecard with HTML info in it, // so show the current page and return show_html_page(); }
show_html_page () { string html = "data:text/html,"; if (gHtmlPage != "") { html += llEscapeURL (gHtmlPage); } else { html += llEscapeURL (HTML_DEFAULT); } // note that this is limited to 1024 characters // See: http://wiki.secondlife.com/wiki/User:Kelly_Linden/lsl_hacks llOwnerSay ("HTML Page Size: " + (string)llStringLength (html)); llSetPrimMediaParams(HTML_FACE_DISPLAY, // Side to display the media on. [PRIM_MEDIA_AUTO_PLAY,TRUE, // Show this page immediately PRIM_MEDIA_CURRENT_URL,html, // The url currently showing PRIM_MEDIA_HOME_URL,html, // The url if they hit 'home' PRIM_MEDIA_HEIGHT_PIXELS,512, // Height/width of media texture will be PRIM_MEDIA_WIDTH_PIXELS,512, // rounded up to nearest power of 2. PRIM_MEDIA_AUTO_SCALE, FALSE, PRIM_MEDIA_CONTROLS, PRIM_MEDIA_CONTROLS_MINI ]); }
default { on_rez (integer start_param) { llResetScript(); }
state_entry () { check_notecards(); }
dataserver (key query_id, string data) { if (query_id != gNCQueryId) { // Not for us return; } if (data == EOF) { // All done show_html_page(); return; }
// Otherwise add the data from the notecard to the CSS string gHtmlPage += data;
// and read the next line gNCLine++; gNCQueryId = llGetNotecardLine(HTML_NOTECARD_NAME, gNCLine); }
changed (integer change) { if (change & CHANGED_INVENTORY) { check_notecards(); } } } </lsl>