Difference between revisions of "User:Kimm Paulino/Scripts1"

From Second Life Wiki
Jump to navigation Jump to search
(added non-physical non-mesh up-down-rotate script)
(added physical non-mesh up-down-rotate script)
Line 708: Line 708:
         {
         {
             llWhisper (PUBLIC_CHANNEL, "Response: " + (string)body);
             llWhisper (PUBLIC_CHANNEL, "Response: " + (string)body);
        }
    }
}
</lsl>
== Smooth, non-mesh up/down/rotate ==
<lsl>
// Makes an object go up and down gradually whilst slowly rotating.
// In order to make it a gradual movement, it uses a physical
// object, which means it might be a little wobbly ...
//
// The value given to the angular velocity parameter is quite critical
// depending on the mass of the object you wish to rotate. You'll need
// a larger value for larger objects otherwise they'll hardly move.
//
// Whilst this appears to work ok for small objects, it is very unpredictable
// and doesn't really work for large objects.
//
//  Kimm Paulino
//  December 2012
//
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;
float DOWN_CORRECTION=1.5;  // Use trial-and-error to correct for SVC-2441
float TIME_PERIOD=0.2;
// Move to 2m above starting position
vector gOffset = <0.0, 0.0, 2.0>;
vector gStartPosition;
float gTimerCount;
// Angular Velocity Details - use values to rotate around the specified axis
// between 0.5 and around 5.0 (or even more for very large objects)
vector gAngularVelocity = <0.0, 0.0, 0.5>;  // For small objects
//vector gAngularVelocity = <0.0, 0.0, 5.0>;  // For large objects
default
{
    state_entry()
    {
        gTimerCount = 0.0;
        // Use physics so that can use MoveToTarget
        gStartPosition = llGetPos ();
        // Stop the object rotating except in Z axis
        llSetStatus(STATUS_ROTATE_X|STATUS_ROTATE_Y, FALSE);
        llSetStatus(STATUS_PHYSICS, TRUE);
        llMoveToTarget (gStartPosition, MOVE_DAMPING);
        llSetTimerEvent (TIME_PERIOD);
    }
    on_rez(integer n)
    {
        llResetScript();
    }
    timer ()
    {
        llSetAngularVelocity (gAngularVelocity, TRUE);
        gTimerCount += TIME_PERIOD;
        if (gTimerCount > MOVE_TIME)
        {
            gTimerCount = 0.0;
           
            if (gState == DOWN)
            {
                gState = GO_UP;
                llMoveToTarget(gStartPosition + gOffset, MOVE_DAMPING);
                gState = UP;
            }
            else if (gState == UP)
            {
                gState = GO_DOWN;
                // Need to compensate (a bit) for SVC-2441
                llMoveToTarget(gStartPosition, MOVE_DAMPING*DOWN_CORRECTION);
                gState = DOWN;
            }
         }
         }
     }
     }

Revision as of 16:41, 28 December 2012

Rez and Sense

<lsl> // Rezzer that will only rez an object (from inventory) if it // can't detect the specified number of objects in the region nearby already. // // Note: Sensing is limitd to a maximum of 96m in all directions. If the // objects pass out of that range, this won't find them. // // Always looks for (and rezzes) the first object found in inventory. // NB: This must have copy permissions if you want to rez it more than once! // // Kimm Paulino, July 2012

integer MAX_REZZED_OBJECTS = 5; vector REZ_POSITION = <1.0, 0.0, 0.0>; // Relative to the rezzing prim, must be less than 10m away vector REZ_ROTATION = <0.0, 0.0, 0.0>; // In degrees

string gObjectName;

rezObject () {

   llRezObject (gObjectName, llGetPos() + REZ_POSITION, ZERO_VECTOR, llEuler2Rot (REZ_ROTATION * DEG_TO_RAD), 0);

}

default {

   on_rez (integer start_param)
   {
       llResetScript();
   }
   
   state_entry ()
   {
       gObjectName = llGetInventoryName (INVENTORY_OBJECT, 0);
       if (gObjectName == "")
       {
           llOwnerSay ("Note to owner: No objects found in this prims inventory");
       }
   }
   
   touch_start (integer num_detected)
   {
       if (gObjectName != "")
       {
           // Trigger a single sensor event for the full range/arc looking for any non-avatar
           // objects with the same name as our rezzing object.
           llSensor (gObjectName, NULL_KEY, (ACTIVE|PASSIVE), 96.0, PI);
       }
   }
   
   sensor (integer num_detected)
   {
       if (num_detected < MAX_REZZED_OBJECTS)
       {
           rezObject();
       }
       else
       {
           llOwnerSay ("Max Objects detected ...");
           // Nothing to do
       }
   }
   
   no_sensor ()
   {
       // None found, so ok to rez one
       rezObject();
   }
   
   changed (integer change)
   {
       if (change & CHANGED_INVENTORY)
       {
           llResetScript();
       }
   }

} </lsl>

Derez After Sitting

<lsl> // This will kill the prim it is in, the specified time after someone // sits on the prim. It will unsit them before derezzing. // // Note: This only works when the prim has a sittarget defined. // But this script does not have to be the thing setting it, // if the sit target is set to <0.0,0.0,0.0> in this script, // then no call to llSitTarget is made - so another script // can do it instead. // // It can optionally derez on unsit too - i.e. when they get up // or derez a defined time after they unsit. // // Note: It doesn't unsit any other avatars sitting on other // prims prior to derezzing, but derezzing will unsit them anyway! // // It will also (optionally) automatically derez itself after a specified // time if noone sits down on it. // // Kimm Paulino, July 2012

// TRUE if want to immediately auto derez when av gets up. // (overrides DEREZ_AFTER_UNSIT_TIMEOUT) integer DEREZ_ON_UNSIT = FALSE;

// Time to derez if noone sits down // (in seconds - 0.0 to disable) float DEREZ_TIMEOUT = 60.0;

// Once an av sits down, this timer starts // and will derez when it times out. // (in seconds - 0.0 to disable) float DEREZ_AFTER_SIT_TIMEOUT = 30.0;

// Once an av gets up, this timer starts // and will derez when it times out. // (in seconds - 0.0 to disable) float DEREZ_AFTER_UNSIT_TIMEOUT = 0.0;

// Position of the sittarget vector SIT_VECTOR = <0.0, 0.0, 0.5>; // In metres - ZERO_VECTOR to disable vector SIT_ROTATION = <0.0, 0.0, 0.0>; // In degrees

key gAvUuid; integer gSitting = FALSE;

default {

   on_rez (integer start_param)
   {
       llResetScript();
   }
   state_entry()
   {
       if (SIT_VECTOR != ZERO_VECTOR)
       {
           llSitTarget (SIT_VECTOR, llEuler2Rot (SIT_ROTATION * DEG_TO_RAD));
       }
       llSetTimerEvent (DEREZ_TIMEOUT);
   }

   timer ()
   {
       if (gAvUuid)
       {
           llUnSit (gAvUuid);
       }
       llSetTimerEvent (0.0);
       llDie();
   }
   
   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)
       {
           gAvUuid = llAvatarOnSitTarget();
           if (gAvUuid)
           {
               // Avatar has just sat down
               gSitting = TRUE;
               llSetTimerEvent (DEREZ_AFTER_SIT_TIMEOUT);
           }
           else if (gSitting)
           {
               // Assume av just got up - it was sitting and now isn't ...
               gSitting = FALSE;
               if (DEREZ_ON_UNSIT)
               {
                   // Want to derez straight away
                   llDie();
               }
               llSetTimerEvent (DEREZ_AFTER_UNSIT_TIMEOUT);
           }
           else
           {
               // Could be unlinking going on
               // or maybe there is another sit target in a linked prim
               // that someone has just sat on or vacated
           }
       }
   }

} </lsl>

Timer Action Template

<lsl> // Basic timer-driven state changing script. // NB: Does not use the LSL ability to define // states, but uses a counter to manage a simple // incrementing state machine. // // Every time interval, it will change its record of state // and you can add code to do something, then it will move // to the next state. // // To add more things, just add more // else if (gState == 4) // { // // and the next // } // statements in the appropriate place. // // If you want the timer interval to be different for different // states, then you could add more llSetTimerEvent() calls // in each state to set the timer for the next one. // // Kimm Paulino // Sept 2012

// Timer interval in seconds float TIMER_INTERVAL = 5.0;

integer gState;

default {

   on_rez (integer start_param)
   {
       llResetScript();
   }
   
   state_entry ()
   {
       gState = 0;
       llSetTimerEvent (TIMER_INTERVAL);
   }
   
   timer ()
   {
       gState ++;
       if (gState == 1)
       {
           // Do the first thing here
       }
       else if (gState == 2)
       {
           // After the timer interval this is next
       }
       else if (gState == 3)
       {
           // and the next
       }
       // add more else if () statements here
       else
       {
           // Once all states are complete, reset the counter ready for the next time
           gState = 0;
       }
   }

} </lsl>

Multiple Notecard Reading Template

<lsl> // Example showing multiple notecard reading. // // Kimm Paulino, Oct 2012

key gNCQueryId; integer gNCLine; integer gNCNumber; string gNCName;

default {

   on_rez (integer start_param)
   {
       llResetScript();
   }
   
   state_entry ()
   {
       gNCNumber = 0;
       gNCLine = 0;
       gNCName = llGetInventoryName (INVENTORY_NOTECARD, gNCNumber);
       if (gNCName != "")
       {
           llOwnerSay ("=== Reading notecard " + gNCName + " (" + (string)gNCNumber + ")");
           gNCQueryId = llGetNotecardLine (gNCName, gNCLine);
       }
   }
   
   dataserver (key query_id, string data)
   {
       if (query_id != gNCQueryId)
       {
           // Not for us
           return;
       }
       // Note: If the notecard contains embedded objects (like scripts, landmarks, etc)
       // then EOF is returned - so this will move onto the next one as if the notecard
       // was empty.
       if (data == EOF)
       {
           // Move to the next notecard
           gNCNumber++;
           gNCLine = 0;
           gNCName = llGetInventoryName (INVENTORY_NOTECARD, gNCNumber);
           if (gNCName != "")
           {
               llOwnerSay ("=== Reading notecard " + gNCName + " (" + (string)gNCNumber + ")");
               gNCQueryId = llGetNotecardLine (gNCName, gNCLine);
           }
           else
           {
               llOwnerSay ("=== Complete");
           }
           // Wait for the next line to be read
           return;
       }
       // Do something with the notecard line
       // Note: Only get first 255 characters of each line
       llOwnerSay (data);
       
       // and read the next line
       gNCLine++;
       gNCQueryId = llGetNotecardLine(gNCName, gNCLine);
   }
   
   changed (integer change)
   {
       if (change & CHANGED_INVENTORY)
       {
           llResetScript();
       }
   }

} </lsl>

Alarm Script

<lsl> // Simple script to trigger at a certain time and perform a function, // in the default case send an IM to the specified avatar // // Notes // Can specify a time in GMT (ie no seasonal changes) or SLT // Uses 24 hour times // Can include touch control if required // Sends the specified message to the specified UUID // // If you want it to do something else, code it in the alarm() function // // Kimm Paulino // Oct 2012 // http://kimmscripts.wordpress.com/

integer gHour = 7; // 24 hour clock integer gMin = 30; integer gSec = 0; integer gGMT = TRUE; // GMT or SL time integer gTouchControl = TRUE; string gMsg = "Wake up!"; key gAv = "00000000-0000-0000-0000-000000000000";

integer gArmed;

setAlarm () {

   // set a timer event for destination time - current time
   float desttime = (float)(gHour*60*60 + gMin*60 + gSec);
   // This is the number of seconds since midnight
   // so the required setting until the destination time
   // will depend on if the time is in the past or not.
   // If we happen to get lucky and 
   float nowtime = llGetWallclock ();
   if (gGMT)
   {
       nowtime = llGetGMTclock ();
   }
   
   if (nowtime >= desttime)
   {
       // Need to add 24 hours before subtracting
       desttime = desttime + 24.0*60.0*60.0;
       llOwnerSay ("Dest: " + (string)desttime + " Now: " + (string)nowtime);
       llSetTimerEvent (desttime - nowtime);
   }
   else
   {
       llOwnerSay ("Dest: " + (string)desttime + " Now: " + (string)nowtime);
       llSetTimerEvent (desttime - nowtime);
   }

}

disableAlarm() {

   llSetTimerEvent (0.0);

}

// This is what happens on the alarm alarm () {

   //llOwnerSay ("Alarm");
   llInstantMessage (gAv, gMsg);

}

default {

   state_entry()
   {
       string zone = " SLT";
       if (gGMT)
       {
           zone = " GMT";
       }
       llOwnerSay ("Alarm set for " + (string)gHour + " h " + (string)gMin + " m" + (string)gSec + " s " + zone);
       gArmed = TRUE;
       setAlarm();
   }
   
   touch_start (integer num_detected)
   {
       if (gTouchControl)
       {
           if (gArmed)
           {
               gArmed = FALSE;
               disableAlarm();
           }
           else
           {
               gArmed = TRUE;
               setAlarm();
           }
       }
   }
   
   timer ()
   {
       alarm();
       // Reset alarm each time, to allow for any drift in time
       setAlarm();
   }

} </lsl>

Simple Channel Relay

<lsl> // Simple relay script. Listens on one channel and relays // everything to the another channel. // // Can be useful for testing things - e.g. if you are listening // on a negative channel in your main script and want a way // to send it test strings, you could drop this in a nearby // prim and have it listen on a chat channel and pass the messages // onto your prim under test. // // Kimm Paulino // Nov 2012

integer gListenChannel = 88; integer gSpeakChannel = -88;

relayMessage (string message) {

   // Only use one of these as required:
   // whisper = <10m; say = 20m; shout > 20m; region = whole region
   //llWhisper (gSpeakChannel, message);
   llSay (gSpeakChannel, message);
   //llShout (gSpeakChannel, message);
   //llRegionSay (gSpeakChannel, message);

}

default {

   on_rez (integer start_param)
   {
       llResetScript();
   }
   state_entry ()
   {
       llListen (gListenChannel, "", "", "");
   }
   
   listen (integer channel, string name, key id, string message)
   {
       if (channel == gListenChannel)
       {
           relayMessage (message);
       }
   }

} </lsl>

HTTP Server and Client Example

These two scripts can be used (once you've hard-coded in the right URL into the client script) to highlight basic prim to prim communications using HTTP without requiring an external server.

<lsl> // Very, very simple HTTP server test script, as seen on // http://wiki.secondlife.com/wiki/LSL_http_server/examples // // Note: It gets a new public URL each time it runs or // when the object moves Sims ... // // So, you'll need to update the URL used by any client objects, // or use some kind of dynamic URL service - there are some around // on the Internet - search/read about HTTP-in // // Kimm Paulino, Nov 2009

default {

   on_rez (integer start_param)
   {
       llResetScript();
   }
   
   state_entry()
   {
       llRequestURL();
   }

   http_request(key id, string method, string body)
   {
       if (method == URL_REQUEST_GRANTED)
       {
           llOwnerSay("URL: " + body);
       }
       else if (method == "GET")
       {
           llOwnerSay ("Handling Response ...");
           llHTTPResponse(id,200,"Hello From Kimm's HTTP Test");
       }
   }

} </lsl>

<lsl> // HTTP Test. This will request the contents of the specified // URL. The idea is that the URL is provided by a companion object // in SL. Note that the URL created by the llRequestURL call changes // when the server object is rezzed or moves sims though ... // // There are some services on the Net that provide a semi-permanent // URL for use with HTTP-in like this though ... // // Kimm Paulino, Nov 2009

string URL = "past in your URL from the server script here";

default {

   touch_start(integer total_number)
   {
       llHTTPRequest (URL, [HTTP_METHOD, "GET"], "");
   }
   
   http_response(key id, integer status, list meta, string body)
   {
       llWhisper (PUBLIC_CHANNEL, "Response: " + (string)body);
   }

} </lsl>

HTTP POST Client and Server Example

This is similar to the above pair of scripts, but uses HTTP POST to send a message from the client to the server. The message is entered using a text box. It also uses the text box to enter in the server's URL when it first starts up.

<lsl> // Very, very simple HTTP server test script, as seen on // http://wiki.secondlife.com/wiki/LSL_http_server/examples // // but configured to accept and display what gets sent to // it via a POST request. // // Note: It gets a new public URL each time it runs or // when the object moves Sims ... // // So, you'll need to update the URL used by any client objects, // or use some kind of dynamic URL service - there are some around // on the Internet - search/read about HTTP-in. // // Also, as this is a demo script, it doesn't do things like clean // up URLs and so on - see the wikis for details. // // Kimm Paulino, Nov 2012

default {

   on_rez (integer start_param)
   {
       llResetScript();
   }
   
   state_entry()
   {
       llRequestURL();
   }

   http_request(key id, string method, string body)
   {
       if (method == URL_REQUEST_GRANTED)
       {
           llOwnerSay("URL: " + body);
       }
       else if (method == "GET")
       {
           llOwnerSay ("Handling Response ...");
           llHTTPResponse(id,200,"Post me something and I'll display it ...");
       }
       else if (method == "POST")
       {
           llOwnerSay ("Received: " + body);
           llHTTPResponse (id, 200, "");
       }
   }

} </lsl>

And the client ...

<lsl> // HTTP Test. This will POST a message to the specified // gUrl. The idea is that the gUrl is provided by a companion object // in SL. Note that the gUrl created by the llHRequestgUrl call changes // when the server object is rezzed or moves sims though ... // // There are some services on the Net that provide a semi-permanent // gUrl for use with HTTP-in like this though ... // // The message is entered using a textbox on touch. When the script // first starts up, you need to paste in the URL of the server prim // using the same text box. // // This is just a demo script. You'll need to do proper listen and // channel handling and so on. // // Kimm Paulino, Nov 2012

string gUrl = ""; integer gChannel;

integer randChannel () { // Based on http://tali.appspot.com/html/scripting/snippets.html // Always leaves 17th bit set (so never a number less than 65535) // Always leaves sign bit unset (so is always positive) integer pos_int = (((integer)llFrand(16384)) << 17) | 65536 | ((integer)llFrand(65535));

   return -pos_int;

}

default {

   on_rez (integer start_param)
   {
       llResetScript();
   }
   
   state_entry ()
   {
       gChannel = randChannel();
       llListen (gChannel, "", "", "");    // Uses simplest and laggiest form of listen/channel handling
   }
   touch_start(integer total_number)
   {
       if (gUrl == "")
       {
           llTextBox (llDetectedKey (0), "Paste in the URL from the server prim and click submit", gChannel);
       }
       else
       {
           llTextBox (llDetectedKey (0), "Type in your message to send to the HTTP server", gChannel);
       }
   }
   
   listen (integer channel, string name, key id, string message)
   {
       if (channel != gChannel)
       {
           return;
       }
       
       if (llSubStringIndex (message, "http://") == 0)
       {
           // this is a new gUrl ...
           gUrl = message;
       }
       else if (gUrl != "")
       {
           llHTTPRequest (gUrl, [HTTP_METHOD, "POST"], message);
       }
       else
       {
           llOwnerSay ("You need to provide the URL of the server prim before sending any messages");
       }
   }
   
   http_response(key id, integer status, list meta, string body)
   {
       if (body != "")
       {
           llWhisper (PUBLIC_CHANNEL, "Response: " + (string)body);
       }
   }

} </lsl>

Smooth, non-mesh up/down/rotate

<lsl> // Makes an object go up and down gradually whilst slowly rotating. // In order to make it a gradual movement, it uses a physical // object, which means it might be a little wobbly ... // // The value given to the angular velocity parameter is quite critical // depending on the mass of the object you wish to rotate. You'll need // a larger value for larger objects otherwise they'll hardly move. // // Whilst this appears to work ok for small objects, it is very unpredictable // and doesn't really work for large objects. // // Kimm Paulino // December 2012 //

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; float DOWN_CORRECTION=1.5; // Use trial-and-error to correct for SVC-2441 float TIME_PERIOD=0.2;

// Move to 2m above starting position vector gOffset = <0.0, 0.0, 2.0>; vector gStartPosition; float gTimerCount;

// Angular Velocity Details - use values to rotate around the specified axis // between 0.5 and around 5.0 (or even more for very large objects) vector gAngularVelocity = <0.0, 0.0, 0.5>; // For small objects //vector gAngularVelocity = <0.0, 0.0, 5.0>; // For large objects

default {

   state_entry()
   {
       gTimerCount = 0.0;
       // Use physics so that can use MoveToTarget
       gStartPosition = llGetPos ();

       // Stop the object rotating except in Z axis
       llSetStatus(STATUS_ROTATE_X|STATUS_ROTATE_Y, FALSE);
       llSetStatus(STATUS_PHYSICS, TRUE);
       llMoveToTarget (gStartPosition, MOVE_DAMPING);
       llSetTimerEvent (TIME_PERIOD);
   }

   on_rez(integer n)
   {
       llResetScript();
   }

   timer ()
   {
       llSetAngularVelocity (gAngularVelocity, TRUE);
       gTimerCount += TIME_PERIOD;
       if (gTimerCount > MOVE_TIME)
       {
           gTimerCount = 0.0;
           
           if (gState == DOWN)
           {
               gState = GO_UP;
               llMoveToTarget(gStartPosition + gOffset, MOVE_DAMPING);
               gState = UP;
           }
           else if (gState == UP)
           {
               gState = GO_DOWN;
               // Need to compensate (a bit) for SVC-2441
               llMoveToTarget(gStartPosition, MOVE_DAMPING*DOWN_CORRECTION);
               gState = DOWN;
           }
       }
   }

} </lsl>

Non-physical, non-mesh up/down/rotate

<lsl> // Makes an object go up or down, gradually, using non-physical movement. // // Unfortunately, non-physical movement is jerky. If you can support it, // a better up/down movement can be had using physics and up/down and // rotate is better using llSetKeyframedmotion. // // Kimm Paulino // December 2012

integer gState; integer DOWN=0; integer GO_UP=1; integer UP=2; integer GO_DOWN=3;

// Period for the up/down movement is MOVE_COUNT * MOVE_TIME integer MOVE_COUNT=40; float MOVE_TIME=0.1;

// Rotation value to use vector gRotationAxis = <0.0, 0.0, 1.0>; float gPiDivisor = 2.0;

// The vector to add to the start position for MOVE_COUNT times vector gOffset = <0.0, 0.0, 0.05>; vector gStartPosition; vector gCurrentPosition; integer gCount; integer gDirection;

default {

   state_entry()
   {
       gCount = 0;
       gDirection = 1;
       gStartPosition = llGetPos ();
       gCurrentPosition = gStartPosition;
       
       // Start the rotation
        llTargetOmega (gRotationAxis, PI/gPiDivisor, 1.0);

       // Stop the object rotating
       llSetTimerEvent (MOVE_TIME);
   }

   on_rez(integer n)
   {
       llResetScript();
   }

   timer ()
   {
       gCount++;
       if (gCount > MOVE_COUNT)
       {
           gCount = 0;
           gDirection = -gDirection;
           
           if (gState == DOWN)
           {
               gState = GO_UP;
               gState = UP;
           }
           else if (gState == UP)
           {
               gState = GO_DOWN;
               gState = DOWN;
           }
       }
       
       gCurrentPosition = gCurrentPosition + (gOffset * gDirection);
       
       llSetPos (gCurrentPosition);
   }

} </lsl>