User:Zai Lynch/Sandbox/LSL Goodies

From Second Life Wiki
Jump to navigation Jump to search


Intro

So just for the record: I'm a complete LSL n00b. So don't expect much from this page. 'Cause I love the Open Source concept, I'd like to share some of my scripts with other SL Residents =)
Since these are contributions to the SL Wiki, all scripts are released under Creative Commons Attribution-Share Alike 3.0 License.


Simple Countdown

This script displays a hovering countdown above the item it's placed in. Need to provide time difference between start and end of the countdown. Another implementation, needing date and time when the countdown is supposed to finish, can be found below.


<lsl> ////////////////////////////////////////////////////// // // // Simple Countdown Script // // released under // // Creative Commons Attribution-Share Alike 3.0 // // by Zai Lynch // // // //////////////////////////////////////////////////////


integer DAYS = 28; // Days until the countdown finishs integer HOURS = 6; // Hours until the countdown finishs integer MINUTES = 42; // Minutes until the countdown finishs integer SECONDS = 12; // Seconds until the countdown finishs string TEXT = "The end of the world will come in"; // Additional text defining the event string FINISHED = "uh oh..."; // Text displayed when event happened vector COLOR = <1,1,1>; // Color of the displayed text


countdown() {

   llSetText(TEXT+"\n"+(string)(SECONDS/86400)+" days, "
       +(string)((SECONDS%86400)/3600)+" hours, "
       +(string)(((SECONDS%86400)%3600)/60)+" minutes, "
       +(string)(((SECONDS%86400)%3600)%60)+ "seconds.",COLOR,1);

}


default {

   state_entry()
   {
       SECONDS = SECONDS + MINUTES * 60 + HOURS * 3600 + DAYS * 86400;
       llSetTimerEvent(1);
       countdown();
   }


   timer()
   {
       if (SECONDS > 0)
       {
           SECONDS = SECONDS - 1;
           countdown();
       }
       else 
       {
           llSetText(FINISHED, COLOR,1);
           llSetTimerEvent(0);
       }
   }

} </lsl>


The next version is based on a timestamp -> unixtime conversion by Trinity Coulter.

<lsl> // _ _ _ // (_) | | | | // __ ____ _ _ | |_ _ _ __ ___| |__ // |_ / _` | | | | | | | '_ \ / __| '_ \ // / / (_| | | | | |_| | | | | (__| | | | // /___\__,_|_| |_|\__, |_| |_|\___|_| |_| // __/ | // |___/ // // released this script under // Creative Commons Attribution-Share Alike 3.0 // // Based on a timestamp -> unixtime // conversion script by Trinity Coulter // taken from // http://tinyurl.com/6hyf4z // at September 4th 2008 //


// Specify time and date in UTC

string Date = "2009.09.04"; // YYYY.MM.DD string Time = "15:44:00"; // hh:mm:ss

string TEXT = "The end of the world will come in"; // Additional text defining the event string FINISHED = "uh oh..."; // Text displayed when event happened vector COLOR = <1,1,1>; // Color of the displayed text


string myGMTTime; integer SECONDS;

countdown() {

   llSetText(TEXT+"\n"+(string)(SECONDS/86400)+" days, "
       +(string)((SECONDS%86400)/3600)+" hours, "
       +(string)(((SECONDS%86400)%3600)/60)+" minutes, "
       +(string)(((SECONDS%86400)%3600)%60)+ "seconds.",COLOR,1);

}


default {

   state_entry()
   {
       myGMTTime = Date +"."+Time;
       string toUnix = "http://www.iwebtool.com/tool/tools/unix_time_converter/unix_time_converter.php?year=" 
       + llGetSubString(myGMTTime,0,3) + "&mon=" + llGetSubString(myGMTTime,5,6) + "&day=" 
       + llGetSubString(myGMTTime,8,9) + "&hour=" + llGetSubString(myGMTTime,11,12) + "&min=" 
       + llGetSubString(myGMTTime,14,15) + "&sec=" + llGetSubString(myGMTTime,17,18);
       SECONDS = llGetUnixTime();
       llHTTPRequest(toUnix,[HTTP_METHOD,"GET"],"");
   }


   http_response(key request_id,integer status, list metadata, string body)
   {
       body = llGetSubString(body, 50, -9);
       SECONDS = (integer)body + 18000 - (2 * llGetUnixTime()) + SECONDS;
       llSetTimerEvent(1); 
   }

   timer()
   {
       if (SECONDS > 0)
       {
           SECONDS = SECONDS - 1;
           countdown();
       }
       else 
       {
           llSetText(FINISHED, COLOR,1);
           llSetTimerEvent(0);
       }
   }

} </lsl>


Basic Notecard-Reader

This script reads notecards placed in the same prim as the script itself.

<lsl> ////////////////////////////////////////////////////// // // // Basic Notecard-Reader // // released under // // Creative Commons Attribution-Share Alike 3.0 // // by Zai Lynch // // // //////////////////////////////////////////////////////


// Constants

integer CHANNEL = 2130214; list MENU = ["Forward", "Backward", "Load", "Play", "Stop", "Time"];

// Variables list gMenuNC; integer gLine = 0; integer gTime = 5; string gNC = ""; key owner;

zlGenerateList(){

   integer i;
   integer n = llGetInventoryNumber(INVENTORY_NOTECARD);
   string name;
   gMenuNC = [];
   if (n > 0){
       for (i=0; i<n; i++){
           name = llGetInventoryName(INVENTORY_NOTECARD, i);
           if (llStringLength(name) < 25)
               gMenuNC = gMenuNC + [name];
           else {
               llSay(0, "Name: "+name + " is to long.");
               llRemoveInventory(name);
               llSay(0, "Notecard removed.");
           }
       }
   }
   if (gNC == "") gNC=llGetInventoryName(INVENTORY_NOTECARD,0);

}


default {

   state_entry(){
       zlGenerateList();
       owner = llGetOwner();
       llListen(CHANNEL, "", owner, "");
       llListen(CHANNEL+1, "", owner, "");
   }
   
   on_rez(integer start_param){
       llResetScript();
   }
   
   changed(integer change){
       if (change & CHANGED_INVENTORY) zlGenerateList();
       else if (change & CHANGED_OWNER) llResetScript();
   }
   
   touch_start(integer num){
       if (llDetectedKey(0) == owner) llDialog(owner," ",MENU,CHANNEL);
   }
   
   listen(integer cha, string name, key id, string msg){
       if (cha == CHANNEL){
           if (msg == "Stop") {
               llSetTimerEvent(0);
           }
           else if (msg == "Play") {
               llSetTimerEvent(gTime); 
               llGetNotecardLine(gNC, gLine); 
           }
           else if (msg == "Forward") {
               gLine = gLine + 1; 
               llGetNotecardLine(gNC, gLine); 
               llSetTimerEvent(0);
           }
           else if ((msg == "Backward") && (gLine > 0)) {
               gLine = gLine - 1; 
               llGetNotecardLine(gNC, gLine); 
               llSetTimerEvent(0);
           }
           else if (msg == "Load") {
               llDialog(owner, " ", gMenuNC, CHANNEL+1);
               return;
           }
           else if (msg == "Time") 
               state listening;
       }
       else {
           gNC = msg;
           gLine = 0;
       }
       llDialog(owner," ",MENU,CHANNEL);
   }
   
   timer(){
       gLine = gLine+1;  
       llGetNotecardLine(gNC, gLine);
   }
   
   dataserver(key qid, string data){
       llSay(0, data);        
   }
   
   state_exit(){
       llSetTimerEvent(0);
   }
       

}

state listening{

   state_entry(){
       llSetTimerEvent(30);
       llListen(0, "", owner, "");
       llSay(0,"Please tell me the desired updatetime in seconds.");
   }
   
   listen(integer cha, string name, key id, string msg){
       gTime = (integer)msg;
       llSay(0, "Time set.");
       state default;
   }    
   
   timer(){
       llSay(0,"Sorry, the request timed out.");
       state default;
   }
   
   state_exit(){
       llSetTimerEvent(0);
       llDialog(owner," ",MENU,CHANNEL);
   }

} </lsl>


Teleport via Map

Set the description of the prim this script is attached to, to: SIMNAME/X/Y/Z
The script will read the description and open the map at the defined position in case it is clicked.
Click and hold the prim clicked for more then 4 seconds in order to re-initialize the script.

<lsl> ////////////////////////////////////////////////////// // // // Teleport via Map // // released under // // Creative Commons Attribution-Share Alike 3.0 // // by Zai Lynch // // // //////////////////////////////////////////////////////

string simname; vector pos; integer touchStartTime;


init() {

   if (llGetObjectDesc() != "")
   {
   string desc = llGetObjectDesc();
   simname = llGetSubString(desc,0,llSubStringIndex(desc,"/")-1);
   desc = llGetSubString(desc,llSubStringIndex(desc,"/")+1,llStringLength(desc)+1);
   integer x = (integer)llGetSubString(desc,0,llSubStringIndex(desc,"/")-1);
   desc = llGetSubString(desc,llSubStringIndex(desc,"/")+1,llStringLength(desc)+1);
   integer y = (integer)llGetSubString(desc,0,llSubStringIndex(desc,"/")-1);
   desc = llGetSubString(desc,llSubStringIndex(desc,"/")+1,llStringLength(desc)+1);
   integer z = (integer)llGetSubString(desc,0,llStringLength(desc)-1);
   pos = <x,y,z>;
   }
   else
   {
       simname="Omidyar";
       pos=<130,86,200>;
   }

}


default {

   state_entry()
   {
       init();
   }
   
   touch_start(integer num_detected)
   {
       touchStartTime = llGetUnixTime();
   }
   
   touch_end(integer num_detected)
   {
       if (llGetUnixTime() > touchStartTime + 4)
       {
           llSay(0,"Resetting...");
           init();
       }
       else 
       llMapDestination(simname, pos, <1,1,1> );
   }

} </lsl>


Buzzer

This script is supposed to be used during meetings so a speaker isn't interrupted by questions while people can still indicate that they would like to speak. Up to 8 people might leave their indication by clicking the item where the script is attached to. Every name will only be listed once.

Click and hold it clicked for more than 3 seconds to delete the upmost name.

<lsl> // _ _ _ // (_) | | | | // __ ____ _ _ | |_ _ _ __ ___| |__ // |_ / _` | | | | | | | '_ \ / __| '_ \ // / / (_| | | | | |_| | | | | (__| | | | // /___\__,_|_| |_|\__, |_| |_|\___|_| |_| // __/ | // |___/ // // released this script under // Creative Commons Attribution-Share Alike 3.0 //

integer time; list q = [];


default {

   state_entry(){
       llSetText("User with questions:",<1,1,1>,1);
   }
   
   on_rez(integer startparam){
       llResetScript();
   }

   touch_start(integer total_number){
       time = llGetUnixTime();
   }

   touch_end(integer num_detected){
       if (llGetUnixTime() < time + 3){
           string name = llDetectedName(0);
           if(!(~llListFindList(q,(list)name))) q += [name];
       }
       else q = llDeleteSubList(q,0,0);
       string text = "User with questions:";
       integer i;
       for (i = 0; ((i < llGetListLength(q))&&(i < 8));i++){
           text += "\n"+llList2String(q,i);
       }
       llSetText(text, <1,1,1>,1);
   } 

} </lsl>

Landmark Replacer

The following two scripts are supposed to be used by shop owners who are moving their store to a new spot. Since most owners box their items with a landmark to their shop, all these landmarks need to be updated after the move. Depending on the number of items, this can become an exhausting exercise. It will still remain an exhausting exercise, even with the use of these scripts, but it will at least make it a little faster. The first script zlLandmarkSender is supposed to be placed in a single prim, which contains the landmark to the new store. Alter the LM variable that way, that it's value equals the exact name of the landmark in the objects inventory. Afterwards, rez all your boxes and place the zlLandmarkCleaner&Replacer script inside them. This script will search for exisiting landmarks and remove them. Afterwards, it will ask the previously rezzed prim (server) for the uptated landmark. When you finished placing your scripts in the old boxes, you can click the initially rezzed serverprim and answer the upcoming question with yes in order to remove all zlLandmarkCleaner&Replacer scripts from the boxes and to de-rez the server prim.

zlLandmarkSender

<lsl> // _ _ _ // (_) | | | | // __ ____ _ _ | |_ _ _ __ ___| |__ // |_ / _` | | | | | | | '_ \ / __| '_ \ // / / (_| | | | | |_| | | | | (__| | | | // /___\__,_|_| |_|\__, |_| |_|\___|_| |_| // __/ | // |___/ // // released this script under // Creative Commons Attribution-Share Alike 3.0 //


// This script provides the landmark(s) the landmarks and sends // the kill code to remove the scripts, once the process finished. // Just add the new landmark to the prim containing this script, // specify the name of the landmark in the LM variable below, // place all the zlLanmarkCleaner&Receiver scripts in the obsolete // boxes. When all scripts are placed, you can click the prim where // this script here is in and answer the upcoming question with "yes".


string LM = "NAME"; // Please enter the correct name of the landmark here.


integer CHANNEL = -1564876734;


default {

   state_entry()
   {
       llListen(CHANNEL, "", "", "Gimme LM plz");
       llListen(CHANNEL + 1, "", llGetOwner(), "");
   }
   
   
   listen(integer cha, string name, key id, string msg)
   {
       if (cha == CHANNEL) llGiveInventory(id, LM);
       else {
           if (msg == "Yes") 
           {
               llShout(CHANNEL, "kill");
               llOwnerSay("All scripts killed. Job done. Removing myself.");
               llDie();
           }
       }
   }
   
   touch_start(integer num)
   {
       if (llDetectedKey(0) == llGetOwner())
       llDialog(llDetectedKey(0), "Kill all scripts?", ["Yes", "No"], CHANNEL+1);
   }

} </lsl>


zlLandmarkCleaner&Receiver

<lsl> // _ _ _ // (_) | | | | // __ ____ _ _ | |_ _ _ __ ___| |__ // |_ / _` | | | | | | | '_ \ / __| '_ \ // / / (_| | | | | |_| | | | | (__| | | | // /___\__,_|_| |_|\__, |_| |_|\___|_| |_| // __/ | // |___/ // // released this script under // Creative Commons Attribution-Share Alike 3.0 //


// This script is supposed to be placed in boxes which // contain obsolete landmarks. It should be added AFTER // the server prim with the zlLandmarkSenderScript is set up.


integer CHANNEL = -1564876734;


default {

   state_entry()
   {   
       integer b = llGetInventoryNumber(INVENTORY_LANDMARK);
       integer a;
       if (b > 0)
       {
       for (a = 0; a < b; a++)
       llRemoveInventory(llGetInventoryName(INVENTORY_LANDMARK,0));
       }
           
       llShout(CHANNEL, "Gimme LM plz");
       llListen(CHANNEL, "", "", "kill");
       
   }
   listen(integer cha, string name, key id, string msg)
   {
       if (llGetOwnerKey(id) == llGetOwner()) llRemoveInventory(llGetScriptName());
   }

} </lsl>

IM Greeter

This is supposed to be a low spam IM greeter.

<lsl> // _ _ _ // (_) | | | | // __ ____ _ _ | |_ _ _ __ ___| |__ // |_ / _` | | | | | | | '_ \ / __| '_ \ // / / (_| | | | | |_| | | | | (__| | | | // /___\__,_|_| |_|\__, |_| |_|\___|_| |_| // __/ | // |___/ // // released this script under // Creative Commons Attribution-Share Alike 3.0 //


string MESSAGE = "Heyas!"; // Message that's supposed to be send.

integer RANGE = 5; // Scans RANGE meter. integer RATE = 5; // Scans every RATE seconds. integer CLEANTIME = 300; // List is cleaned every CLEANTIME seconds. integer ABSENTTIME = 300; // Time ava is allowed to

                         // be out of scanning range before getting removed
                         // from the list.

list MyAvatarList = []; // List that stores avas and timestamps.


default{

   state_entry(){
       llSensorRepeat("", "", AGENT, RANGE, PI, RATE); // start scanner
       llSetTimerEvent(CLEANTIME); // start timer for cleanup
   }
   
   on_rez(integer start){
       llResetScript(); // Reset script when greeter rezzed
   }
   
   sensor(integer num_detected){
       integer i = 0; 
       for (;i < num_detected; i++){ // for each detected ava do
           key ava = llDetectedKey(i); // grab the key
           integer index = llListFindList(MyAvatarList, (list)ava); // grab position in list           
           if(~index){ // if ava in list
               // update the timestamp
               MyAvatarList = llListReplaceList(MyAvatarList, [llGetUnixTime()], index+1, index+1);
           }
           else {  // else
               MyAvatarList += [ava, llGetUnixTime()]; // add ava to list
               llInstantMessage(ava, MESSAGE); // greet ava via IM
           }
       }
   }
   
   timer(){ // start to clean obsolete entries
       integer length = llGetListLength(MyAvatarList);
       integer i = 1;
       integer time = llGetUnixTime(); // store current time
       for (; i < length; i += 2) { // for all timestamps
           if (time - llList2Integer(MyAvatarList,i) > ABSENTTIME){ // compare timestamp with current time
               // delete ava from list when absent for to long
               MyAvatarList = llDeleteSubList(MyAvatarList, i-1, i); 
               i -= 2;  // offset
               length -= 2; // offset
           }
       }
   }

}</lsl>


Simple Radar

This is supposed to be a simple radar, scanning the surrounding (96m) and showing distance as well as agent infos. The output is very similar to (and inspired by) a functionality of Mystical Cookie's famous Mysti-Tool HUD.

<lsl> // _ _ _ // (_) | | | | // __ ____ _ _ | |_ _ _ __ ___| |__ // |_ / _` | | | | | | | '_ \ / __| '_ \ // / / (_| | | | | |_| | | | | (__| | | | // /___\__,_|_| |_|\__, |_| |_|\___|_| |_| // __/ | // |___/ // // released this script under // Creative Commons Attribution-Share Alike 3.0 //


// global constant float RATE = 0.5; // sets update rate in seconds integer off = TRUE;

default{

   state_entry(){
       // some hack to make it run in no script areas
       llReleaseControls();
       llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
       // remove hovertext
       llSetText("", <1,1,1>,1);
       // tell the owner that the device is currently off
       llOwnerSay("Radar off. Click to activate.");
   }

   sensor(integer num){
       // prepare output string
       string text = "Detected "+(string)(num)+" Resident";
       // distinguish for plural or singular
       if (num == 1) text += ":\n";
       else text += "s:\n";
       // define a local info variable
       integer info;
       // define a local index variable
       integer i = 0;
       // store the current position so we can later calculate the distance
       vector pos = llGetPos();
       // loop through all detected Residents
       for (; i < num; i++){
           // add name and distance to the output string
           text += llDetectedName(i)+" ("+ (string)((integer)llVecDist(llDetectedPos(i),pos)) +"m) ";
           // retrieve information about that Resident and store it in the variable
           info = llGetAgentInfo(llDetectedKey(i));
           // add letter W in case the Resident is walking
           if (info & AGENT_WALKING) text += "W";
           // add letter F in case the Resident is flying (or hovering)
           if (info & AGENT_FLYING) text += "F";
           // add letter S in case the Resident is sitting
           if (info & AGENT_SITTING) text += "S";
           // add letter T in case the Resident is typing (AND has typing animation)
           if (info & AGENT_TYPING) text += "T";
           // add letter M in case the Resident is in mouselook
           if (info & AGENT_MOUSELOOK) text += "M";
           // add a linebreak
           text += "\n";
       }
       // display the text above the prim
       llSetText(text, <1.0, 1.0, 1.0>, 1.0);
   }
   
   touch_start(integer num){
       if (off){
           // scan the surrounding for Residents
           llSensorRepeat("", "", AGENT, 96.0, PI, RATE);
           // give a notice that it's on
           llOwnerSay("Radar on.");
           // set the check variable to FALSE so we know that it's on
           off = FALSE;
       }
       else llResetScript();
   }
   
   // this part belongs to the no script area hack
   run_time_permissions(integer perms){
       llTakeControls(CONTROL_FWD, 0, 1);
   }
   
   no_sensor() {
         llSetText("Detected 0 Residents.", <1.0, 1.0, 1.0>, 1.0);
   }

}</lsl>

Zai landing.png