User:Kimm Paulino/Scripts1

From Second Life Wiki
Jump to navigation Jump to search

Rez and Sense

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

Derez After Sitting

// 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
            }
        }
    }
}

Timer Action Template

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

Multiple Notecard Reading Template

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

Alarm Script

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

Simple Channel Relay

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

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.

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

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.

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

And the client ...

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

Smooth, non-mesh up/down/rotate

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

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

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