Difference between revisions of "User:Kimm Paulino/Scripts1"
Jump to navigation
Jump to search
Kimm Paulino (talk | contribs) m |
Kimm Paulino (talk | contribs) m |
||
(15 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
== Rez and Sense == | == Rez and Sense == | ||
< | <syntaxhighlight lang="lsl2"> | ||
// Rezzer that will only rez an object (from inventory) if it | // Rezzer that will only rez an object (from inventory) if it | ||
// can't detect the specified number of objects in the region nearby already. | // can't detect the specified number of objects in the region nearby already. | ||
Line 77: | Line 77: | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
== Derez After Sitting == | == Derez After Sitting == | ||
< | <syntaxhighlight lang="lsl2"> | ||
// This will kill the prim it is in, the specified time after someone | // This will kill the prim it is in, the specified time after someone | ||
// sits on the prim. It will unsit them before derezzing. | // sits on the prim. It will unsit them before derezzing. | ||
// | // | ||
// Note: This only works when the prim has a sittarget defined. | // 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. | // It can optionally derez on unsit too - i.e. when they get up | ||
// or derez a defined time after they unsit. | |||
// | // | ||
// It | // 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 | // Kimm Paulino, July 2012 | ||
// TRUE if want to immediately auto derez when av gets up. | |||
float DEREZ_TIMEOUT = | // (overrides DEREZ_AFTER_UNSIT_TIMEOUT) | ||
float DEREZ_AFTER_SIT_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 | // Position of the sittarget | ||
vector SIT_VECTOR = <0.0, 0.0, 0.5>; // In metres - | 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 | vector SIT_ROTATION = <0.0, 0.0, 0.0>; // In degrees | ||
key gAvUuid; | key gAvUuid; | ||
integer gSitting = FALSE; | |||
default | default | ||
Line 116: | Line 136: | ||
state_entry() | state_entry() | ||
{ | { | ||
llSitTarget (SIT_VECTOR, llEuler2Rot (SIT_ROTATION * DEG_TO_RAD)); | if (SIT_VECTOR != ZERO_VECTOR) | ||
{ | |||
llSitTarget (SIT_VECTOR, llEuler2Rot (SIT_ROTATION * DEG_TO_RAD)); | |||
} | |||
llSetTimerEvent (DEREZ_TIMEOUT); | llSetTimerEvent (DEREZ_TIMEOUT); | ||
} | } | ||
Line 140: | Line 163: | ||
{ | { | ||
// Avatar has just sat down | // Avatar has just sat down | ||
gSitting = TRUE; | |||
llSetTimerEvent (DEREZ_AFTER_SIT_TIMEOUT); | llSetTimerEvent (DEREZ_AFTER_SIT_TIMEOUT); | ||
} | } | ||
else | else if (gSitting) | ||
{ | { | ||
// Assume av just got up | // Assume av just got up - it was sitting and now isn't ... | ||
gSitting = FALSE; | |||
if (DEREZ_ON_UNSIT) | if (DEREZ_ON_UNSIT) | ||
{ | { | ||
// Want to derez straight away | |||
llDie(); | 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 | |||
} | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
== Timer Action Template == | |||
<syntaxhighlight lang="lsl2"> | |||
// 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; | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
== Multiple Notecard Reading Template == | |||
<syntaxhighlight lang="lsl2"> | |||
// 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(); | |||
} | } | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
== | == Alarm Script == | ||
( | <syntaxhighlight lang="lsl2"> | ||
// 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(); | |||
} | |||
} | |||
</syntaxhighlight> | |||
== Simple Channel Relay == | |||
<syntaxhighlight lang="lsl2"> | |||
// 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); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
== 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. | |||
<syntaxhighlight lang="lsl2"> | |||
// 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"); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<syntaxhighlight lang="lsl2"> | |||
// 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); | |||
} | |||
} | |||
</syntaxhighlight> | |||
== 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. | |||
<syntaxhighlight lang="lsl2"> | |||
// 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, ""); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
And the client ... | |||
<syntaxhighlight lang="lsl2"> | |||
// 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); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
== Smooth, non-mesh up/down/rotate == | |||
<syntaxhighlight lang="lsl2"> | |||
// 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; | |||
} | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
== Non-physical, non-mesh up/down/rotate == | |||
<syntaxhighlight lang="lsl2"> | |||
// 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); | |||
} | |||
} | |||
</syntaxhighlight> |
Latest revision as of 12:14, 18 May 2016
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);
}
}